BAT美团滴滴java面试大纲(带答案版)之四:多线程Lock

继续面试大纲系列文章。java

 

  这是多线程的第二篇。面试

  多线程就像武学中对的吸星大法,理解透了用好了能够得道成仙,俯瞰芸芸众生;而滥用则会遭其反噬。编程

  在多线程编程中要渡的第二个“劫”,则是Lock。在不少时候,包括面试、包括实际项目应用,咱们都会拿来和synchronized对比一番。安全

  咱们知道,多线程的核心思想是经过增长线程数量来并发的运行,来提升效率,也就是数量决胜论,而不是质量决胜(提升每一个线程的处理能力)。多线程编程中面临的最大挑战,是如何解决多个线程同时修改一个公用的变量所带来的变量值不肯定性问题。顺着这个思路分析,经常使用办法,无非就是,要么对变量动手,在一个线程修改时,变量值被锁定。要么是对修改的操做动手,在该段代码执行时,对其加锁,其余线程不能够在同一时刻进入该段代码执行。多线程

  同synchronized同样,Lock,也是实现了后一种办法。只不过,实现方式,有所不一样。并发

 

Lock

 

  1. 问:你平时涉及到多线程编程多很少?谈谈你对Lock锁的理解
  2. 分析:最好对比着synchronized来说
  3. 答:
    1.   在多线程编程中,为了达到线程安全的目的,咱们每每经过加锁的方式来实现。Lock锁是java代码级别来实现的,相对于synchronizedd在功能性上,有所增强,主要是,公平锁,轮询锁,定时锁,可中断锁等,还增长了多路通知机制(Condition),能够用一个锁来管理多个同步块。另外在使用的时候,必须手动的释放锁。
    2. 详细分析:
      1. Lock锁的实现,主要是借助于队列同步器(咱们经常见到的AQS)来实现。它包括一个int变量来表示状态;一个FIFO队列,来存储获取资源的排队线程。
      2. 当一个线程申请资源时,就是是获取当前的同步状态,并判断是否可符合预期,若是是,则经过CAS操做,来修改上述Int变量标识的同步状态。若是否,则线程进入队列排队(这是在通常状况,在使用tyrLock时,是直接返回获取锁失败)。
          1. 锁有独占锁和共享锁。独占锁就是在同一时刻,只容许同一个线程持有该锁;共享锁实现的时候和独占锁稍有不一样,不是简单的修改同步状态(好比1和0),而是获取这个值,当值大于0时,即标识获取共享锁成功(隐含意思是每一个线程获取锁成功后,这个值减1)。这里附上独占锁的实现源码(源码片断来自《java并发编程的艺术》,并加上本身的注释):
            public class Mutex implements Lock {  
              
                // 静态内部类,自定义同步器  
                private static class Sync extends AbstractQueuedSynchronizer{  
                    // 该方法用于判断当前锁是否在独占模式下被占用状态  
                    protected boolean isHeldExclusively(){  
                        return getState() == 1;  
                    }  
              
                    // 获取锁!!! 
                    public boolean tryAcquire(int acquires){ 
                  //典型的CAS原子操做,若是初始状态为0,能够得到锁
            if (compareAndSetState(0, 1)){ setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } //释放锁,将当前状态设置为0 protected boolean tryRelease(int releases){ if (getState() == 0){ throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0); return true; } // 返回一个Condition,每一个condition都包含了一个condition队列 ,这个后续再说 Condition newCondition(){ return new ConditionObject(); } }

             

      3. Lock锁中,支持可中断的锁,实现原理是,队列中的等待线程,能够响应其余线程发起的中断信号,抛出InterruptdException异常。
      4. 关于同步队列,须要了解,获取同步状态失败的线程,被包装为Node节点后,加入队列尾,这个操做是CAS操做,以保证线程安全,失败就死循环重试;而队列首节点,则是当前持有锁的线程。该节点一旦释放锁,会唤醒后继节点。
      5. 关于唤醒,是这样的,每一个在同步队列中的阻塞线程,都处于自旋的状态,不断的尝试获取锁。这样,当首节点释放锁唤醒后继线程后,被唤醒的线程,还须要判断是否前继线程是首线程,是则获取同步状态(锁)成功。

  4.扩展:Condition,多路通知机制  ui

  

  1. 在Synchronized锁中,提供了wait、notify、notifyAll等方法,实现了等待/通知模式。那么在lock中,由Condition配合,也实现了相似的模式。
  2. 其实现实质是,一个Condition包含一个等待队列,定义多个Condition,那就有多个等待队列,和上文提到的同步队列配合使用。同步队列-等待队列模型请参考下图:
  3. 在上述模型中,调用await方法,至关于把同步队列首节点(持有锁的线程),移动到等待队列。调用signal方法唤醒阻塞的线程,则是将对应Condition等待队列里的首节点(等待时间最长),移入同步队列。
  4. 还有一点须要补充,就是线程的唤醒,调用signal能够正常唤醒;在其余线程中终止线程,也同样会唤醒,只不过唤醒后,只是抛出InterruptException异常。
  5. 若是你看的爽,请点击右下角的“推荐”,是对小端坚持分享原创的最大鼓励。也能够关注小端的我的公众号 :   pnxsxb  ,会分享更多的原创技术文章。

欢迎扫描如下二维码关注公众号:小端有话说:spa

相关文章
相关标签/搜索