Reent和Synchronize区别
ReentrantLock和Synchronize都是一个可重入锁
区别在于ReentrantLock是jdk提供的一个锁,它有一下Synchronize不具备的特性,比如锁中断,锁多个对象通过Condition,公平锁。它有两个内部类,FairSync,和NoFairSync,都间接继承了AbstractQueuedSynchronizer同步队列类,使用AQS实现了加锁和解锁的功能。
AQS是J.U.C包的基础组件,是实现一系列并发工具的基础。
它里面提供了一个阻塞队列(链表实现的)来保存没有获取到锁的线程,每一个节点如何保存当前线程和前后节点的引用之外还需要保存一个waitStatus,表示节点的状态。提供了一个状态变量state,表示锁的状态。其中大量使用了CAS操作。
当A,B线程进行竞争的时候,
1、A线程通过CAS操作修改statue成功,说明A竞争到了锁,继续执行
2、如果A操作失败,说明B获取了锁,那么A执行下面的流程
3、生成一个新节点,通过CAS操作插入到队尾,并且它不会立即挂起,而是执行自旋,因为插入到队尾的这段时间,B可能已经执行完了释放了锁了,所以判断当前节点的前一个节点是不是head节点,如果是的话,尝试获取锁,获取成功,那么就不挂起了,直接执行。如果失败或者不是head节点,执行下一步。
4、判断前一个节点的状态是不是SIGNAL,只有是SINGNAL的时候才会挂起。如果前一个节点的waitStatus==0,那么通过CAS操作将状态改成SINGNAL,如果waitSatus > 0 说明是CANCELLED的状态,删除这个节点,如果waitStatus==SIGNAL,那么调用LockSupport.park()方法挂起。
5、释放锁的时候从队列里找到第一个SIGNAL状态的节点唤醒,唤醒的线程需要判断是否中断,需要和新来的线程进行竞争锁,这体现了非公平性。
Synchronize是JVM提供的一个关键字,是一个可重入锁,通过在需要同步的字节码前后加上monitorenter和monitorexit,当线程执行到monitorenter的时候就需要去获取对象的锁,如果获取成功了,就继续执行,失败了就阻塞。也维护了一个计数器来记录重入次数。不过现在对这个Synchronize优化的比较好了,引入了自旋锁,适应性自旋,轻量级锁,偏向锁,锁粗化,锁消除等等,提高了synchronize性能。
轻量级锁,为了在有并发但是没有竞争的情况下替代重量级锁,减少系统消耗,操作的核心是对象头的Mark Word,这块区域平时保存的是hashCode,gc年龄等。
当一个线程准备后去轻量级锁的时候,先判断他有没有被锁定,如果没有锁定,那么会在栈帧里面创建一块内存区域,复制这个markword,然后通过cas操作将原来的MarkWord更改成执行栈帧中的指针,状态改成轻量级锁定。成功说明获取到了轻量级锁,失败说明发生了竞争,需要膨胀成重量级锁,后面的线程全部阻塞。
释放锁的时候,将栈帧中的MarkWord更新回去,如果成功,就成功释放,如果失败,说明有别的线程竞争,释放锁的同时需要唤醒被挂起的线程。