内容简介:本文翻译和原创各占一半,所以还是厚颜无耻归类到原创好了...java 5 其中一个令人振奋的改进是新增了支持原子操作的类型,例如
本文翻译和原创各占一半,所以还是厚颜无耻归类到原创好了...
https://howtodoinjava.com/jav...
java 5 其中一个令人振奋的改进是新增了支持原子操作的类型,例如 AtomicInteger
, AtomicLong
等。在多线程环境中进行简单的自增自减操作时,这些原子类能帮助你减少很多用于多线程同步的复杂代码。这些原子类依赖于 CAS (compare and swap) 算法,接下来我们会讨论 CAS 这个概念。
乐观锁和悲观锁
传统的锁机制,例如 java 的 synchronized
关键字,他代表了 java 中悲观锁技术,保证了某一时刻仅有一个线程能访问同步代码/方法。 synchronized
能够很好地工作,却有着 (相对) 比较大的性能开销。
乐观锁 (相对悲观锁) 对性能会有很大的帮助。他的核心思想是:你寄希望于在没有冲突的情况下完成一次更新操作,使用乐观锁技术更新时会进行 “冲突检测” 来判断是否有其他的线程干扰,若是 (有其他线程干扰) 则视本次更新操作失败,一般会进行重试 (可以了解一下CAS自旋)。 Compare and Swap
就是典型的乐观锁技术。
CAS 算法
CAS 算法会先 对一个内存变量(位置) V 和一个给定的值进行比较 A
,如果相等,则 用一个新值 B 去修改这个内存变量(位置)
。上述过程会作为一个原子操作完成 (intel处理器通过 cmpxchg
指令系列实现)。CAS 原子性保证了新值的计算是基于上一个有效值,期间如果内存变量(位置) V
被其他线程更新了,本线程的 CAS 更新操作将会失败。CAS 操作必须告诉调用者成功与否,可以返回一个 boolean 值来表示,或者返回一个从内存变量读到的值 (应该是上一次有效值)
CAS 操作数有三个:
- 内存变量(位置) V ,表示被更新的变量
- 线程上一次读到的旧值 A
- 用来覆盖 V 的新值 B
CAS 表示:“我认为现在 V 的值还是之前我读到的旧值 A,若是则用新值 B 覆盖内存变量 V,否则不做任何动作并告诉调用者操作失败”。CAS 是一项乐观锁技术,他在更新的时候总是希望能成功 (没有冲突),但也能检测出来自其他线程的冲突和干扰
Java 中的 Compare and Swap
这里我们关注一下 ReentrantLock
锁定和解锁那部分的源码
//ReentrantLock.lock() public void lock() { sync.lock(); }
他依赖了其内部类 Sync
的 lock()
,以下是内部类 Sync
(继承了队列同步器 AQS)
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; abstract void lock(); ................
Sync
还是个抽象类,一般 new ReentrantLock()
时创建的是 NonfairSync
// ReentrantLock的构造方法 public ReentrantLock() { sync = new NonfairSync(); }
下面就是 NonfairSync
的 lock()
方法了
final void lock() { if (compareAndSetState(0, 1)) // 1 setExclusiveOwnerThread(Thread.currentThread()); // 2 else acquire(1); // 3 }
-
1 中的
compareAndSetState()
承继自队列同步器 AQS,封装了 CAS 指令。因为是NonfairSync
非公平锁,所以一上来就尝试抢占锁:给定旧值 0 并希望用新值 1 去更新内存变量 State。若更新成功则视为获取锁成功,并执行 2 - 2 成功完成了 CAS 操作 (没错,当你使用 CAS 指令成功把 State 从 0 更新成 1 便视为获取锁,就是这么简单粗暴 ╮(╯▽╰)╭ ),把当前线程设为独占线程
- 3 操作失败 (被人抢先获取锁(╯`□′)╯╧╧),进行 acquire 操作再次尝试获取锁,若还是不行,则把当前线程加入 AQS 等待队列,由 AQS 来管理队列中等待线程的阻塞和唤醒,具体代码就不贴出来了,AQS 的源码多处使用到 CAS 指令,有兴趣的同学可以查看
锁用完了要释放,下面贴出 unlock()
方法
// ReentrantLock.unlock() public void unlock() { sync.release(1); }
这里还是依赖了 sync, release()
是 AQS 的通用方法,其内部调用了 tryRelease()
(由 Sync 类实现),这里直接贴出 Sync 的 tryRelease()
protected final boolean tryRelease(int releases) { // releases 参数的值是上面传进来的 1 int c = getState() - releases; // 1 if (Thread.currentThread() != getExclusiveOwnerThread()) // 1.5 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 2 free = true; setExclusiveOwnerThread(null); } setState(c); // 3 return free; }
ReentrantLock
Summary
AQS 队列同步器以及 java.util.concurrent
下各种锁和原子类都运用到的 CAS 算法,有时间的同学建议阅读加深印象。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 通俗易懂--决策树算法、随机森林算法讲解(算法+案例)
- 限流算法之漏桶算法、令牌桶算法
- 什么是Paxos算法?Paxos算法是区块链核心算法之一
- 一文读懂对称加密算法、非对称加密算法和Hash算法
- 算法(六):图解贪婪算法
- 算法篇03:排序算法
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head First jQuery
Ryan Benedetti , Ronan Cranley / O'Reilly Media / 2011-9 / USD 39.99
Want to add more interactivity and polish to your websites? Discover how jQuery can help you build complex scripting functionality in just a few lines of code. With Head First jQuery, you'll quickly g......一起来看看 《Head First jQuery》 这本书的介绍吧!