1.深入学习CAS底层原理
2.expect的源码源码安装方法
3.shell编程100例(附PDF下载)
4.Python Flask 开发,Flask 的源码 Swagger 神器 —— Flask-RESTX
深入学习CAS底层原理
什么是CAS
CAS是Compare-And-Swap的缩写,意思为比较并交换。源码以AtomicInteger为例,源码其提供了compareAndSet(intexpect,源码intupdate)方法,expect为期望值(被修改的源码永恒之塔 源码值在主内存中的期望值),update为修改后的源码值。compareAndSet方法返回值类型为布尔类型,源码修改成功则返回true,源码修改失败返回false。源码
举个compareAndSet方法的源码例子:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicIntegeratomicInteger=newAtomicInteger(0);booleanresult=atomicInteger.compareAndSet(0,1);System.out.println(result);System.out.println(atomicInteger.get());}}上面例子中,通过AtomicInteger(intinitialValue)构造方法指定了AtomicInteger类成员变量value的源码初始值为0:
publicclassAtomicIntegerextendsNumberimplementsjava.io.Serializable{ ......privatevolatileintvalue;/***CreatesanewAtomicIntegerwiththegiveninitialvalue.**@paraminitialValuetheinitialvalue*/publicAtomicInteger(intinitialValue){ value=initialValue;}......}接着执行compareAndSet方法,main线程从主内存中拷贝了value的源码副本到工作线程,值为0,源码并将这个值修改为1。源码如果此时主内存中value的值还是为0的话(言外之意就是没有被其他线程修改过),则将修改后的副本值刷回主内存更新value的值。所以上面的例子运行结果应该是true和1:
将上面的例子修改为:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicIntegeratomicInteger=newAtomicInteger(0);booleanfirstResult=atomicInteger.compareAndSet(0,1);booleansecondResult=atomicInteger.compareAndSet(0,1);System.out.println(firstResult);System.out.println(secondResult);System.out.println(atomicInteger.get());}}上面例子中,main线程第二次调用compareAndSet方法的时候,value的值已经被修改为1了,不符合其expect的值,所以修改将失败。上面例子输出如下:
CAS底层原理查看compareAndSet方法源码:
/***Atomicallysetsthevalueto{ @codenewValue}*ifthecurrentvalue{ @code==expectedValue},*withmemoryeffectsasspecifiedby{ @linkVarHandle#compareAndSet}.**@paramexpectedValuetheexpectedvalue*@paramnewValuethenewvalue*@return{ @codetrue}ifsuccessful.Falsereturnindicatesthat*theactualvaluewasnotequaltotheexpectedvalue.*/publicfinalbooleancompareAndSet(intexpectedValue,intnewValue){ returnU.compareAndSetInt(this,VALUE,expectedValue,newValue);}该方法通过调用unsafe类的compareAndSwapInt方法实现相关功能。compareAndSwapInt方法包含四个参数:
this,当前对象;
valueOffset,value成员变量的内存偏移量(也就是内存地址):
privatestaticfinallongvalueOffset;static{ try{ valueOffset=unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));}catch(Exceptionex){ thrownewError(ex);}}expect,期待值;
update,更新值。最大游戏源码网
所以这个方法的含义为:获取当前对象value成员变量在主内存中的值,和传入的期待值相比,如果相等则说明这个值没有被别的线程修改过,然后将其修改为更新值。
那么unsafe又是什么?它的compareAndSwapInt方法是原子性的么?查看该方法的源码:
/***AtomicallyupdatesJavavariableto{ @codex}ifitiscurrently*holding{ @codeexpected}.**<p>Thisoperationhasmemorysemanticsofa{ @codevolatile}read*andwrite.CorrespondstoCatomic_compare_exchange_strong.**@return{ @codetrue}ifsuccessful*/@HotSpotIntrinsicCandidatepublicfinalnativebooleancompareAndSetInt(Objecto,longoffset,intexpected,intx);该方法并没有具体Java代码实现,方法通过native关键字修饰。由于Java方法无法直接访问底层系统,Unsafe类相当于一个后门,可以通过该类的方法直接操作特定内存的数据。Unsafe类存在于sun.msic包中,JVM会帮我们实现出相应的汇编指令。Unsafe类中的CAS方法是一条CPU并发原语,由若干条指令组成,用于完成某个功能的一个过程。原语的执行必须是连续的,在执行过程中不允许被中断,不会存在数据不一致的问题。
getAndIncrement方法剖析了解了CAS原理后,我们回头看下AtomicInteger的getAndIncrement方法源码:
/***Atomicallyincrementsthecurrentvalue,*withmemoryeffectsasspecifiedby{ @linkVarHandle#getAndAdd}.**<p>Equivalentto{ @codegetAndAdd(1)}.**@returnthepreviousvalue*/publicfinalintgetAndIncrement(){ returnU.getAndAddInt(this,VALUE,1);}该方法通过调用unsafe类的getAndAddInt方法实现相关功能。继续查看getAndAddInt方法的源码:
/***Atomicallyaddsthegivenvaluetothecurrentvalueofafield*orarrayelementwithinthegivenobject{ @codeo}*atthegiven{ @codeoffset}.**@paramoobject/arraytoupdatethefield/elementin*@paramoffsetfield/elementoffset*@paramdeltathevaluetoadd*@returnthepreviousvalue*@since1.8*/@HotSpotIntrinsicCandidatepublicfinalintgetAndAddInt(Objecto,longoffset,intdelta){ intv;do{ v=getIntVolatile(o,offset);}while(!weakCompareAndSetInt(o,offset,v,v+delta));returnv;}结合源码,我们便可以很直观地看出为什么AtomicInteger的getAndIncrement方法是线程安全的了:
o是AtomicInteger对象本身;offset是AtomicInteger对象的成员变量value的内存地址;delta是需要变更的数量;v是通过unsafe的getIntVolatile方法获得AtomicInteger对象的成员变量value在主内存中的值。dowhile循环中的逻辑为:用当前对象的值和var5比较,如果相同,说明该值没有被别的线程修改过,更新为v+delta,并返回true(CAS);否则继续获取值并比较,直到更新完成。指标源码网站dmi
CAS的缺点CAS并不是完美的,其存在以下这些缺点:
如果刚好while里的CAS操作一直不成功,那么对CPU的开销大;
只能确保一个共享变量的原子操作;
存在ABA问题。
CAS实现的一个重要前提是需要取出某一时刻的数据并在当下时刻比较交换,这之间的时间差会导致数据的变化。比如:thread1线程从主内存中取出了变量a的值为A,thread2页从主内存中取出了变量a的值为A。由于线程调度的不确定性,这时候thread1可能被短暂挂起了,thread2进行了一些操作将值修改为了B,然后又进行了一些操作将值修改回了A,这时候当thread1重新获取CPU时间片重新执行CAS操作时,会发现变量a在主内存中的值仍然是A,所以CAS操作成功。
解决ABA问题那么如何解决CAS的ABA问题呢?由上面的阐述课件,光通过判断值是否相等并不能确保在一定时间差内值没有变更过,所以我们需要一个额外的指标来辅助判断,类似于时间戳,版本号等。
JUC为我们提供了一个AtomicStampedReference类,通过查看它的构造方法就可以看出,除了指定初始值外,还需指定一个版本号(戳):
/***Createsanew{ @codeAtomicStampedReference}withthegiven*initialvalues.**@paraminitialReftheinitialreference*@paraminitialStamptheinitialstamp*/publicAtomicStampedReference(VinitialRef,intinitialStamp){ pair=Pair.of(initialRef,initialStamp);}我们就用这个类来解决ABA问题,首先模拟一个ABA问题场景:
publicclassAtomticIntegerTest{ publicstaticvoidmain(String[]args){ AtomicReference<String>atomicReference=newAtomicReference<>("A");newThread(()->{ //模拟一次ABA操作atomicReference.compareAndSet("A","B");atomicReference.compareAndSet("B","A");System.out.println(Thread.currentThread().getName()+"线程完成了一次ABA操作");},"thread1").start();newThread(()->{ //让thread2先睡眠2秒钟,确保thread1的ABA操作完成try{ TimeUnit.SECONDS.sleep(2);}catch(InterruptedExceptione){ e.printStackTrace();}booleanresult=atomicReference.compareAndSet("A","B");if(result){ System.out.println(Thread.currentThread().getName()+"线程修改值成功,当前值为:"+atomicReference.get());}},"thread2").start();}}运行程序,输出如下:
使用AtomicStampedReference解决ABA问题:
publicclassAtomicIntegerextendsNumberimplementsjava.io.Serializable{ ......privatevolatileintvalue;/***CreatesanewAtomicIntegerwiththegiveninitialvalue.**@paraminitialValuetheinitialvalue*/publicAtomicInteger(intinitialValue){ value=initialValue;}......}0程序输出如下:
expect的皮皮盘搜源码源码安装方法
expect是在tcl基础上创建起来的,它提供了一些tcl所没有的命令,如果是源码安装的话首先需要安装tcl。spawn命令激活一个Unix程序来进行交互式的运行。send命令向进程发送字符串,expect 命令等待进程的某些字符串。
下载tcl源码包 wget /tcl/tcl8.4.-src.tar.gz
解压并编译安装tcl tar zxvf tcl8.4.-src.tar.gz cd tcl8.4./unix/ ./configure --prefix=/usr/tcl --enable-shared make make install 如果编译成功没报错如下图
安装完毕以后进入tcl源代码的根目录,把子目录unix下面的tclUnixPort.h copy到子目录generic中,expect的安装过程还需要用 cp unix/tclUnixPort.h generic/.
下载expect源码包 wget /projects/expect/files/Expect/5./expect5..tar.gz
解压并编译安装expect tar zxvf expect5..tar.gz cd expect5. ./configure --prefix=/usr/expect --with-tcl=/usr/tcl/lib --with-tclinclude=../tcl8.4./generic/ make make install
安装完成之后做一个软连接 ln -s /usr/tcl/bin/expect /usr/expect/bin/expect
加入环境变量并测试 echo 'export PATH=$PATH:/usr/expect/bin' /etc/profile
shell编程例(附PDF下载)
1、编写hello world脚本
2、通过位置变量创建 Linux 系统账户及密码
3、备份日志
4、一键部署 LNMP(RPM 包版本)
5、监控内存和磁盘容量,小于给定值时报警
6、猜数字游戏
7、检测本机当前用户是否为超级管理员,如果是管理员,则使用 yum 安装 vsftpd,如果不是,则提示您非管理员
8、编写脚本检测当前用户是否为超级管理员,并根据结果安装 vsftpd
9、编写脚本自动创建账户及配置密码
、输入三个数并进行升序排序
、石头、剪刀、布游戏
、编写脚本测试 ..4.0/ 整个网段中哪些主机处于开机状态,哪些处于关机状态
、编写脚本测试同一网段的主机开机与关机状态
、编写脚本测试网段主机状态,支持多进程版本
、编写脚本显示进度条
、创建动态时针进度条显示功能
、9*9 乘法表
、实时显示 eth0 网卡发送数据包流量
、秒杀系统源码资料使用 user.txt 文件自动创建账户并配置密码
、编写批量修改文件扩展名脚本
、使用 expect 工具自动安装 httpd 软件
、一键部署 LNMP(源码安装版本)
、编写快速克隆 KVM 虚拟机脚本
、点名器脚本功能实现
、查看远程连接本机的 IP 数量
、计算 1+2+3+...+ 的和
、统计指定时间内的 apache 请求量
、统计指定时间内的访问 IP 地址
、打印棋盘格式的国际象棋
、统计远程 IP 访问本机 apache 的次数
、统计 Linux 登录账户数量
、统计并显示/var/log 文件的名称与数量
、为脚本添加解释器信息
、自动化部署 varnish 源码包软件
、编写 nginx 启动脚本
、自动完成磁盘分区、格式化与挂载
、优化 Linux 内核参数
、分割 Nginx 日志文件,避免单文件过大
、检测 MySQL 连接数量
、根据 md5 校验检测文件完整性
、检测 MySQL 服务状态
、备份 MySQL 数据的 shell 脚本
、将文件中的小写字母转换为大写字母
、自动生成 SSH 密钥文件
、检查特定软件包是否已安装
、监控 HTTP 服务器返回码
、自动配置防火墙规则与开启服务
、脚本自动创建逻辑卷
、显示 CPU 厂商信息
、删除目录下大小为 0 的文件
、查找僵尸进程并处理
、判断年份是否为闰年
、生成随机密码,使用 urandom 版本
、生成随机密码,使用字串截取版本
、生成随机密码,使用 UUID 版本( 进制密码)
、验证用户名与密码正确性
、循环验证用户名与密码正确性
、Shell 脚本的 fork 炸弹
、批量下载文件(包含 PDF、、视频)
、列出当前所有账户的用户名称
、根据目录路径,自动将目录打包为 tar 文件
、创建回旋镖版的进度条显示功能
、安装 LAMP 环境(yum 版本)
、循环关闭局域网中所有主机
、获取本机 MAC 地址
、自动配置 rsyncd.conf 文件
、修改 Linux 系统最大文件打开数量
、设置 Python 自动命令补齐功能
、自动修改计划任务配置文件
、循环创建三位数字的文本文件(-)
、提取和处理 passwd 和 shadow 文件中的信息
、统计 passwd 文件中 root 的出现次数
、统计 Linux 进程信息数量
、根据论坛积分判断用户等级
、识别用户输入数据类型(字母、数字或其他)
、创建数字版的进度条显示功能
、打印斐波那契数列
、判断输入是 Yes 或 NO
、列出本机所有开放端口
、删除 UID 大于等于 的普通用户
、使用脚本控制虚拟机启动与关闭
、调整虚拟机内存参数的 shell 脚本
、查看 KVM 虚拟机网卡信息
、无密码修改虚拟机网卡 IP 地址
、破解虚拟机密码,实现无密码登陆
、Shell 脚本处理信号,避免无法终止脚本的情况
、一键部署 memcached
、一键配置 VNC 远程桌面服务器,无密码版本
、关闭 SELinux
、查看虚拟机磁盘与 CPU 使用量信息
、使用 shell 脚本打印图形
、根据当前时间显示问候语,可设置开机启动
、读取账户名称并保存到数组中
、检查文件或目录存在性
、打印各种格式的时间
、使用 egrep 过滤 MAC 地址
、计算双色球数字中奖概率
、生成签名私钥与证书
、使用 awk 编写的 wc 程序
资源汇总:
整理了丰富的学习资料,包括视频、电子书、PPT 等,欢迎大家免费领取!
资料包含:
- 视频资料
- 电子书资料
资料已打包,无需套路,直接访问即可获取。
欢迎关注公众号 @开源Linux!
更多干货请访问我的个人网站:Linux学习指南
如本文对您有帮助,欢迎点赞、收藏、转发给朋友,支持持续创作!
Python Flask 开发,Flask 的 Swagger 神器 —— Flask-RESTX
在构建Python Web应用时,Flask是一个轻量级的选择,它允许开发者以最小的投入快速搭建应用。而当涉及到构建RESTful API时,Flask-RESTX库提供了方便的方法来定义、编写和查看API文档。
Flask-RESTX是Flask框架的扩展,集成Swagger,这是一个强大的API文档工具。Swagger规范和完整框架用于生成、描述、调用和可视化RESTfulWeb服务的API文档。
安装Flask-RESTX很简单,通过pip命令即可完成。确保Flask已经在开发环境中安装,若未安装,使用相应命令进行安装。
快速开始,创建简单Flask应用并引入Flask-RESTX。这里有个例子,实现一个简单的API,包含一个HelloWorld资源类,提供GET请求响应。使用@api.expect('name')装饰器指定期望参数。
定义API文档时,使用装饰器和注解。文档自动出现在Swagger用户界面中。定义期望参数,使用api.expect装饰器。定义数据模型,使用api.model方法。模型可在API资源中使用。
Flask-RESTX提供高级功能,例如异常处理、错误处理器等。通过Flask错误处理机制定义异常处理器,返回适当HTTP状态码和错误信息。
Flask-RESTX的官方社区活跃在GitHub上,提供源代码和问题跟踪器。社区成员分享使用经验和最佳实践,解决遇到问题。
总结,Flask-RESTX是Flask框架的强大扩展,集成Swagger提供丰富的API文档支持,易于设计、实现和维护RESTfulAPI。无论是初学者还是有经验的开发者,都能从Flask-RESTX的易用性和强大功能中受益。
通过本文了解,已具备构建Flask应用的基础知识。实践是学习的最好方式,动手尝试,创建自己的Flask应用吧!