如何理解Java中眼花缭乱的各种并发锁?( 二 )


在 Java 中可以通过构造函数初始化公平锁 , 如代码所示:
/*** 创建一个可重入锁 , * true 表示公平锁,* false 表示非公平锁 。* 默认非公平锁 */Lock lock = new ReentrantLock(true);非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转 , 或者某个线程一直得不到锁的饥饿状态 。

如何理解Java中眼花缭乱的各种并发锁?

文章插图
【如何理解Java中眼花缭乱的各种并发锁?】在 Java 中 synchronized 关键字是非公平锁 , ReentrantLock默认也是非公平锁,如代码所示:
/*** 创建一个可重入锁,true 表示公平锁,false 表示非公平锁 。默认非公平锁*/Lock lock = new ReentrantLock(false);4)一个线程中的多个流程,是否获取同一把锁的情况如果一个线程中的多个流程能获取同一把锁,就使用可重入锁,如果线程的多个流程不能获取通一把锁 , 就是用不可重入锁 。可重入锁又称为递归锁 , 是指同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁 。
对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁 。对于synchronized而言,也是一个可重入锁 。可重入锁的一个好处是可一定程度避免死锁 。以 synchronized 为例,来看这样一段代码:
public synchronized void mehtodA() throws Exception{// Do some magic tingsmehtodB();}public synchronized void mehtodB() throws Exception{// Do some magic tings}在这段代码中 methodA() 调用 methodB(),如果一个线程调用methodA() 已经获取了锁再去调用 methodB() 就不需要再次获取锁了,这就是可重入锁的特性 。如果是不可重入锁的话,mehtodB() 可能不会被当前线程执行 , 可能造成死锁 。
5)某个线程锁住同步资源失败,是否不阻塞的情况如果某个线程锁住同步资源失败,但是希望这个线程不阻塞,就可以使用自旋锁或者自适应自旋锁 。自旋锁是指线程在没有获得锁时不是被直接挂起 , 而是执行一个忙循环,这个忙循环就是所谓的自旋 。
如何理解Java中眼花缭乱的各种并发锁?

文章插图
自旋锁的目的是为了减少线程被挂起的几率,因为线程的挂起和唤醒也都是耗资源的操作 。如果锁被另一个线程占用的时间比较长,即使自旋了之后当前线程还是会被挂起 , 忙循环就会变成浪费系统资源的操作,反而降低了整体性能 。因此自旋锁是不适应锁占用时间长的并发情况的 。在 Java 中,AtomicInteger 类就有自旋的操作,来看这样一段代码:
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, v + delta));return v;}循环条件调用compareAndSwapInt()方法 , 被称为CAS操作,如果失败就会一直循环获取当前 value 值然后重试,这个过程叫自旋 。在JDK1.6引入了自适应自旋 , 这个就比较智能了,自旋时间不再固定 , 由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定 。如果虚拟机认为这次自旋也很有可能再次成功那就会次序较多的时间,如果自旋很少成功,那以后可能就直接省略掉自旋过程,避免浪费处理器资源 。
6)线程竞争同步资源时 , 细节流程是否发生变化的情况JDK1.6 为了提升性能减少获得锁和释放锁所带来的消耗,引入了4种锁的状态:无锁、偏向锁、轻量级锁和重量级锁,它会随着多线程的竞争情况逐渐升级,但不能降级 。
如何理解Java中眼花缭乱的各种并发锁?

文章插图
如果多个线程中,只有一个线程能修改资源成功,其他资源只是重试 , 不锁住资源,称为无锁状态,其实就是乐观锁 。第一个线程访问加锁的资源自动获取锁 , 不存在多线程竞争的情况,资源偏向于第一个访问锁的线程,每次访问线程不需要重复获取锁,这种状态称为偏向锁 。偏向锁的实现是通过控制对象Mark Word的标志位来实现的 , 如果当前是可偏向状态,需要进一步判断对象头存储的线程 ID 是否与当前线程 ID 一致,如果一致直接进入 。当线程竞争变得比较激烈时 , 偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式等待上一个线程释放锁 。但如果线程并发进一步加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了第三个线程访问的时候,轻量级锁就会膨胀为重量级锁,重量级锁会使除了当时拥有锁的线程以外的所有线程都阻塞 。升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其余线程都会处于阻塞等待状态 。在 Java 中,synchronized 关键字内部实现原理就是这样一个锁升级的过程 。

推荐阅读