在进行更近一步的了解Java锁的知识以前,咱们须要先了解与锁有关的两个概念 CAS 与 AQS。关注个人公众号「Java面典」了解更多 Java 相关知识点。html
CAS函数,是比较并交换函数,它是原子操做函数。算法
CAS 是基于乐观锁的原理进行操做的。它老是认为本身能够成功完成操做。当多个线程同时使用 CAS 操做一个变量时,只有一个会胜出,并成功更新,其他均会失败。失败的线程不会被挂起,仅是被告知失败,而且容许再次尝试或放弃操做。多线程
CAS 会致使“ABA 问题”。CAS 算法实现一个重要前提须要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差内会致使数据的变化。并发
ABA 例子:好比说一个线程 one 从内存位置 V 中取出 A,这时候另外一个线程 two 也从内存中取出 A,而且two 进行了一些操做变成了 B,而后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操做发现内存中仍然是 A,而后 one 操做成功。尽管线程 one 的 CAS 操做成功,可是不表明这个过程就是没有问题的。框架
ABA 解决方案:部分乐观锁的实现是经过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操做时,都会带上一个版本号,一旦版本号和数据的版本号一致就能够执行修改操做并对版本号执行+1 操做,不然就执行失败。由于每次操做的版本号都会随之增长,因此不会出现 ABA 问题,由于版本号只会增长不会减小。函数
AQS即AbstractQueuedSynchronizer (抽象的队列式的同步器),AQS 定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如经常使用的ReentrantLock/Semaphore/CountDownLatch。ui
AQS 维护了一个 volatile int state(表明共享资源)和一个 FIFO 线程等待队列(多线程争用资源被阻塞时会进入此队列)。这里 volatile 是核心关键词,具体 volatile 的语义,在此不述。state 的访问方式有三种:
getState()
setState()
compareAndSetState()线程
计数器:同步器的实现是 ABS 核心,以 ReentrantLock 为例,state 初始化为 0,表示未锁定状态。A 线程 lock()时,会调用 tryAcquire() 独占该锁并将 state+1。此后,其余线程再 tryAcquire() 时就会失败,直到 A 线程 unlock() 到 state=0(即释放锁)为止,其它线程才有机会获取该锁。code
可重入锁:在 A 线程释放锁以前,A 线程本身是能够重复获取此锁的(state 会累加),这就是可重入锁的实现。
但要注意,获取多少次就要释放多么次,这样才能保证 state 是能回到零态的
。htm
AQS 定义了两种资源共享方式:
通常来讲,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现 tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared 中的一种便可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如 ReentrantReadWriteLock.
AQS 只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现。自定义同步器实现时主要实现如下几种方法:
1.isHeldExclusively():该线程是否正在独占资源。只有用到 condition 才须要去实现它。
2.tryAcquire(int):独占方式,尝试获取资源。成功则返回 true,失败则返回 false。
3.tryRelease(int):独占方式,尝试释放资源。成功则返回 true,失败则返回 false。
4.tryAcquireShared(int):共享方式,尝试获取资源。负数表示失败;0 表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
5.tryReleaseShared(int):共享方式,尝试释放资源。若是释放后容许唤醒后续等待结点返回true,不然返回 false。
Java多线程并发03——什么是线程上下文,线程是如何调度的