关于Lock与synchronized关键字在锁的处理上的重要差别
表层差别:
1. 锁的获取方式:前者是通过程序代码的方式由开发者手工获取,后者是通过JVM来获取(无需开发者干预)
2. 具体实现方式:前者是通过Java代码的方式来实现,后者是通过JVM底层来实现 (无需开发者关注)
3. 锁的释放方式:前者务必通过unlock()方法在finally块中手工释放,后者是通过JVM来释放(无需开发者关注)
4. 锁的具体类型:前者提供了多种,如公平锁、非公平锁,后者与前者均提供了可重入锁
深层理解Lock:
传统上,我们可以通过synchronized关键字 + wait + notify/notifyAll、来实现多个线程之间的协调与通信,整个过程都是由JVM
* 来帮助我们实现的;开发者无需(也是无法)了解底层的实现细节
* 从JDK 5开始,并发包提供了Lock,Condition( await与signal/signalAll)来实现多个线程之间的协调与通信,整个过程都是由开发
* 者来控制的,而且相比于传统方式,更加灵活,功能也更加强大
*
*Thread.sleep与await(或是object的wait方法)的本质区别: sleep方法本质上不会释放锁,而await会释放锁,并且在signal后,
* 还需要重新获得锁才能继续执行(该行为与object的wait方法完全一致)
*
* AQS:AbstractQueuedSynchronizer(抽象的队列式的同步器)
* 详细介绍移步https://www.cnblogs.com/waterystone/p/4920797.html
*关于AQS与synchronized关键字之间的关系:
* 1. synchronized关键字在底层的c++实现中,存在两个重要的数据结构(集合)∶waitSet,EntryList
* 2. waitSet中存放的是调用了Object的wait方法的线程对象(被封装成了c++的Node对象)
* 3. EntryList中存放的是陷入到阻塞状态、需要获取monitor的那些线程对象
* 4.当一个线程被notify后,它就会从waitset中移动到EntryList中。
* 5。进入到EntryList后,该线程依然需要与其他线程争抢monitor对象
* 6。如果争抢到,就表示该线程获取到了对象的锁,它就可以以排他方式执行对应的同步代码。
*
* 1.AQS中存在两种队列,分别是Condition对象上的条件队列,以及AQS本身的阻塞队列
* 2.这两个队列中的每一个对象都是Node实例(里面封装了线程对象)
* 3。当位于Condition条件队列中的线程被其他线程signal后,该线程就会从条件队列中移动到AQS的阻塞队列中。
* 4、位于AQS阻塞队列中的Node对象本质上都是由一个双向链表来构成的。
* 5。在获取AQS锁时,这些进入到阻塞队列中的线程会按照在队列中的排序先后尝试获取。
* 6.当AQS阻塞队列中的线程获取到锁后,就表示该线程已经可以正常执行了
* 7.陷入到阻塞状态的线程,依然需要进入到操作系统的内核态,进入阻塞(park方法实现)
可重入锁:ReentrantLock
* ReentrantLock通过将继承AQS的子类Sync作为类成员变量来实现锁,sync实现AQS的抽象方法来管理同步状态。
*
* 而 unlock 可以看做对 state 执行“减法”操作,当 state 为 0 时,表示当前没有线程占用资源。
* 对于ReentrantLock来说,其执行逻辑如下所示:
* 1. 尝试获取对象的锁,如果获取不到(意味着已经有其他线程持有了锁,并且尚未释放),那么它就会进入到AQS的阻塞队列当中。
* 2.如果获取到,那么根据锁是公平锁还是非公平锁来进行不同的处理
* 2.1如果是公平锁,那么线程会直接放置到AQS阻塞队列的未尾
* 2.2如果是非公平锁,那么线程会首先尝试进行CAS计算,如果成功,则直接获取到锁;
* 如果失败,则与公平锁的处理方式一致,被放到阻塞队列末尾
* 3当锁被释放时(调用了unlock方法),那么底层会调用release方法对state成员变量值进行减一操作,如果减一后,state值不为0,
* 那么release操作就执行完毕;如果减一操作后,state值为O,则调用LockSupport的unpark方法唤醒该线程后的等待队列中的
* 第一个后继线程,将其唤醒,使之能够获取到对象的领‘(release时,对于公平锁与非公平锁的处理逻辑是一致的);
* 之所以调用release方法后state值可能不为零,原因在于ReentrantLock是可重入锁,表示线程可以多次调用lock方法,
* 导致每调用一次,state值都会加一。
*
* 对于ReentrantLock来说,所谓的上锁,本质上就是对AQS中的state成员变量的操作︰对该成员变量+1,表示上锁;
* 对该成员变量-1,表示释放锁。
sync类分析:

sync两个重要的内部类:FairSync与NonfairSync,分别实现公平/非公平锁
(1)我们走一遍这个ReentranLock.lock()源码流程:

默认是非公平锁
先尝试获取资源,如果获取不到再执行acquire(),
此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。获取到资源后,线程就可以去执行其临界区代码了。
分析下每个方法含义:
跟踪下tryAcquire(arg),该方法返回nonfairTryAcquire()方法
分析上面代码,目前如果没有锁(state==0)就尝试获取锁,获取不到就是被同步队列线程抢先了,或者当前线程有锁,主人还不是自己,就返回false.
(2)我们走下公平锁作为对比
最开始没有进行获取锁的尝试,再看一下tryAcquire()
看到连tryAcquire()也见了前提,!hasQueuePrdercess() 说明如果此时同步队列为空时,才进行获取锁的尝试。
读写锁:
* 关于ReentrantReadwriteLock的操作逻辑:
* volatile int state有32位,高16位表示读,低16位表示写。
* 读锁:
* 1.在获取读锁时,会尝试判断当前对象是否拥有了写锁,如果已经拥有,则直接失败。
* 2. 如果没有写锁,就表示当前对象没有排他锁,则当前线程会尝试给对象加锁
* 3. 如果当前线程已经持有了该对象的锁,那么直接将读锁数量加1
* 写锁:
* 1.在获取写锁时,会尝试判断当前对象是否拥有了锁(读锁与写锁),如果已经拥有且持有的线程并非当前线程,直接失败。
* 2.如果当前对象没有被加锁,那么写锁就会为为当前对象上锁,并且将写锁的个数加1.
* 3. 将当前对象的排他锁线程持有者设为自己。
原文:https://www.cnblogs.com/wangid3/p/14150468.html