锁专题(1)java 常见锁介绍,高级程序员必知必会( 二 )
文章插图
输入图片说明
自旋锁本身是有缺点的 , 它不能代替阻塞 。
自旋等待虽然避免了线程切换的开销 , 但它要占用处理器时间 。 如果锁被占用的时间很短 , 自旋等待的效果就会非常好 。 反之 , 如果锁被占用的时间很长 , 那么自旋的线程只会白浪费处理器资源 。
所以 , 自旋等待的时间必须要有一定的限度 , 如果自旋超过了限定次数(默认是10次 , 可以使用-XX:PreBlockSpin来更改)没有成功获得锁 , 就应当挂起线程 。
自旋锁的实现原理同样也是CAS , AtomicInteger中调用unsafe进行自增操作的源码中的do-while循环就是一个自旋操作 , 如果修改数值失败则通过循环来执行自旋 , 直至修改成功 。
public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}
自旋锁在JDK1.4.2中引入 , 使用-XX:+UseSpinning来开启 。
JDK 6中变为默认开启 , 并且引入了自适应的自旋锁(适应性自旋锁) 。
自适应意味着自旋的时间(次数)不再固定 , 而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定 。 如果在同一个锁对象上 , 自旋等待刚刚成功获得过锁 , 并且持有锁的线程正在运行中 , 那么虚拟机就会认为这次自旋也是很有可能再次成功 , 进而它将允许自旋等待持续相对更长的时间 。 如果对于某个锁 , 自旋很少成功获得过 , 那在以后尝试获取这个锁时将可能省略掉自旋过程 , 直接阻塞线程 , 避免浪费处理器资源 。
在自旋锁中另有三种常见的锁形式:TicketLock、CLHlock和MCSlock , 本文中仅做名词介绍 , 不做深入讲解 , 感兴趣的同学可以自行查阅相关资料 。
无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁这四种锁是指锁的状态 , 专门针对 synchronized 的 。
详情见 synchronized
公平锁、非公平锁公平锁(Fair)加锁前检查是否有排队等待的线程 , 优先排队等待的线程 , 先来先得 。
公平锁是指多个线程按照申请锁的顺序来获取锁 , 线程直接进入队列中排队 , 队列中的第一个线程才能获得锁 。 公平锁的优点是等待锁的线程不会饿死 。 缺点是整体吞吐效率相对非公平锁要低 , 等待队列中除第一个线程以外的所有线程都会阻塞 , CPU唤醒阻塞线程的开销比非公平锁大 。
非公平锁(Nonfair)加锁时不考虑排队等待问题 , 直接尝试获取锁 , 获取不到自动到队尾等待 。
非公平锁是多个线程加锁时直接尝试获取锁 , 获取不到才会到等待队列的队尾等待 。 但如果此时锁刚好可用 , 那么这个线程可以无需阻塞直接获取到锁 , 所以非公平锁有可能出现后申请锁的线程先获取锁的场景 。
非公平锁的优点是可以减少唤起线程的开销 , 整体的吞吐效率高 , 因为线程有几率不阻塞直接获得锁 , CPU不必唤醒所有线程 。 缺点是处于等待队列中的线程可能会饿死 , 或者等很久才会获得锁 。
ReentrantLock 锁内部提供了公平锁与分公平锁内部类之分 , 默认是非公平锁 , 如:
public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
例子直接用语言描述可能有点抽象 , 这里作者用从别处看到的一个例子来讲述一下公平锁和非公平锁 。
文章插图
输入图片说明
如上图所示 , 假设有一口水井 , 有管理员看守 , 管理员有一把锁 , 只有拿到锁的人才能够打水 , 打完水要把锁还给管理员 。 每个过来打水的人都要管理员的允许并拿到锁之后才能去打水 , 如果前面有人正在打水 , 那么这个想要打水的人就必须排队 。 管理员会查看下一个要去打水的人是不是队伍里排最前面的人 , 如果是的话 , 才会给你锁让你去打水;如果你不是排第一的人 , 就必须去队尾排队 , 这就是公平锁 。
但是对于非公平锁 , 管理员对打水的人没有要求 。 即使等待队伍里有排队等待的人 , 但如果在上一个人刚打完水把锁还给管理员而且管理员还没有允许等待队伍里下一个人去打水时 , 刚好来了一个插队的人 , 这个插队的人是可以直接从管理员那里拿到锁去打水 , 不需要排队 , 原本排队等待的人只能继续等待 。
如下图所示:
文章插图
- 现状|程序员现状揭秘:平均年薪20.36万,Java人才需求量最大
- 苹果|iPhone13迎来变化!或回归指纹解锁,这几点备受用户喜爱
- 自动任务|赶在三星 S21 发布之前实现语音解锁
- 线下连锁商|暴涨130%!荷兰、西班牙、比利时百年商超在考拉海购逆势增长
- 长安街|北京 长安街西城段将来单车无法落锁
- 程序员学英语第1天——JavaScript 程序测试的介绍1
- 三年Java开发,刚从美团、京东、阿里面试归来,分享个人面经
- 《深入理解Java虚拟机》:对象创建、布局和访问全过程
- 现场|逛展看未来丨展会现场还能这么玩?带你解锁萌娃的逛展“姿势”
- iPhone13迎来变化!或回归指纹解锁,这几点备受用户喜爱