锁的实现

在关于锁的面试过程中,一般主要问Synchronized和ReentrantLock的实现原理,更有甚者会问读写锁

场景对话: 面试官:都了解Java中的什么锁?

我:比如Synchronized和ReentrantLock...

面试官:那好,你先说说Synchronized的实现原理吧。

我:嗯,Synchronized是JVM实现的一种锁,在同步块前后锁的获取和释放分别是monitorenter和monitorexit指令,这两条指令都需要一个reference类型的参数来指明要锁定和解锁的对象,[如果Java程序中的Synchronized明确指定了对象参数,那就是这个对象的reference。如果没有,那就根据Synchronized修饰的实力方法或者类方法去取对应的对应的对象实例或者类实例作为锁对象],该锁在实现上分为了偏向锁、轻量级锁和重量级锁,其中偏向锁在1.6是默认开启的,轻量级锁在多线程竞争的情况下会膨胀成重量级锁,有关锁的数据都保存在对象头中...[具体见轻量级所偏向锁和对象的创建过程]。(嗯,说了一大堆,面试官也没打断我)

面试官:哦,嗯,理解的还挺透彻,那你说说ReentrantLock的实现吧...

我:ReentrantLock是基于AQS[抽象队列同步器]实现的重入锁。

面试官:什么是重入锁?Synchronized是不是重入锁?

我:重入锁是指已经占有锁的线程能对同步快重复加锁,不过在释放锁的时要求释放次数和加锁次数一样。Synchronized也是可重入的。

面试官:OK,那什么是AQS呢?

我:在AQS内部维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列,资源的共享方式有两种独占共享,ReentrantLock 是独占资源的。(state初始化为0表示资源空闲, 占有锁state+1 释放锁state-1)

线程发现state为0时,通过CAS指令修改该state的值,修改成功的线程表示获取到该锁,没有修改成功则通过一个Waiter对象封装线程,添加到等待队列中,并挂起等待被唤醒&&%%(又说了一堆)。

[具体可以参考博客http://www.cnblogs.com/waterystone/p/4920797.html 详细介绍了AQS的原理]

面试官:能说说CAS指令的原理么?

我:CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false。

CAS是通过unsafe类的compareAndSwap方法实现的(心里得意的一笑)

bool CAS (int &V,int A,int B){

    if(V == A){

       V = B;

       return true;

  }

  return false;

}

面试官:哦,好的,那你知道这个compareAndSwap方法的参数的含义的么?

我:(这是在逼我啊...努力的回想,因为我真的看过啊)我想想啊,这个方法看的时间有点久远了,第一个参数是要修改的对象(object),第二个参数是对象中要修改变量的偏移量(valueOffSet),第三个参数是修改之前的值(except),第四个参数是预想修改后的值(update)....(说出来之后都有点佩服自己,这个都记得,不过面试官好像还是不肯放过我...)

面试官:嗯,对的,那你知道操作系统级别是如何实现的么?

我:(我去你大爷...)我只记得X86中有一个cmp开头的指令,具体的我忘记了...

面试官:嗯,好,你知道CAS指令有什么缺点么?

我:哦,CAS的缺点是存在ABA问题。

面试官:怎么讲?

我:就是一个变量V,如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它仍然是A,那能说明它的值没有被其他线程修改过了吗?如果在这段期间它的值曾经被改成了B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。

面试官:那怎么解决?

我:(有完没完了啊...我的心里是崩溃的)针对这种情况,java并发包中提供了一个带有标记的原子引用类"AtomicStampedReference",它可以通过控制变量值的版本来保证CAS的正确性。[concurrentHashMap中涉及跨段操作时好像也是这么实现的]

面试官:嗯,好的,你觉得与Sychronized相比ReentrantLock有什么优点。

我:....我能喝口水么,主要有以下三项

1:等待可中断:当持有锁的线程长期不释放锁时,正在等待的线程可以选择放弃等待。

2:可实现公平锁(多线程等待同一个锁时按照申请锁的时间从早到晚以此获得锁):Sychronized是非公平的,ReentrantLock默认也是非公平的,但是可以通过带布尔类型的构造函数来设置为公平锁。

3:锁绑定多条件:Sychronized如果要实现多于一个条件关联的时候,就不得不额外地添加锁。而ReentrantLock只需要多次调用newCondition()方法即可。

面试官:理解得蛮详细的,你知道读写锁么?

我:读写锁用的不多,就没研究了。(我就怕被问读写锁,因为一直没去看)

面试官:好吧,有机会去看看,今天面试到此为止...

读写锁的一些特性:[对同步块的操作无非是读和写,一般的锁被一个线程获取后,其他的线程都进不来。

读写锁将读和写操作分别加锁,当同步块被加了写锁后其他线程不能进行读写操作,当同步块被加了读锁后,其他线程能进行读操作,不能进行写操作]

具体的关于读写锁的问题大家自行搜索...

 

推荐一篇关于轻量级,重量级,偏向锁的博客:http://blog.csdn.net/asialiyazhou/article/details/76098607

posted @ 2017-09-08 14:59  WegYcx  阅读(214)  评论(0编辑  收藏  举报