21道并发编程面试题(9)

    ABA问题,如果将原来A的值改为了B,然后又改回了A,虽然最终结果没有发生改变,但是在过程中是对该数据进行了修改操作

    解决该问题:在Java中并发包下有一个原子类:AtomicStampedReference,在该类当中通过版本控制判断值到底是否被修改

    解释:如果对值进行了更改则版本号+1,那么在CAS当中不仅仅对比变量的值,还要对比版本号,如果值和版本号都相等则代表没有被修改,如果有一方不相等代表进行过更改

    那么就从主内存中重新刷新数据到工作内存然后循环对比,直到成功为止~

16.AQS

  AQS:全称AbstractQueueSynchronizer,抽象队列同步器,这个类在java.util.concurrent.locks包下

            它是一个底层同步工具类,比如CountDownLatch,Sammphore,ReentrantLock,ReentrantReadWriteLock等等都是基于AQS

    底层三个内容:

      1.state(用于计数器)

       2.线程标记(哪一个线程加的锁)

       3.阻塞队列(用于存放阻塞线程)

  AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,如下图所示。AQS为一系列同步器依赖于一个单独的原子变量(state)的同步器提供了一个非常有用的基础。子类们必须定义改变state变量的protected方法,这些方法定义了state是如何被获取或释放的。

    

21道并发编程面试题

   J.U.C是基于AQS实现的,AQS是一个同步器,设计模式是模板模式。

  核心数据结构:双向链表 + state(锁状态)

  底层操作:CAS

    

21道并发编程面试题

17.ReentrantLock底层实现

  ReentrantLock是基于AQS的,AQS是Java并发包中众多同步组件的构建基础,它通过一个int类型的状态变量state和一个FIFO队列来完成共享资源的获取,线程的排队等待等。AQS是个底层框架,采用模板方法模式,它定义了通用的较为复杂的逻辑骨架,比如线程的排队,阻塞,唤醒等,将这些复杂但实质通用的部分抽取出来,这些都是需要构建同步组件的使用者无需关心的,使用者仅需重写一些简单的指定的方法即可(其实就是对于共享变量state的一些简单的获取释放的操作)。

  无参构造器(默认为非公平锁) 

public ReentrantLock() { sync = new NonfairSync();//默认是非公平的 }

    sync是ReentrantLock内部实现的一个同步组件,它是Reentrantlock的一个静态内部类,继承于AQS;

  带布尔值的构造器(是否公平)

public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();//fair为true,公平锁;反之,非公平锁 }

    看到了吧,此处可以指定是否采用公平锁,FailSync和NonFailSync亦为Reentrantlock的静态内部类,都继承于synchronized;

18.ReentrantLock和synchronized之间的区别

synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果

synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间

synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁

synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法

synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现

synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放

19.ReentrantReadWriteLock(读写锁)

  相比java中的锁(Lock in java)里Lock实现,读写锁更复杂一些;

  假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时第一个资源没有任务问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其他线程对该资源进行读或写(也就是说:读和读能共享,读和写不能共享,写和写不能共享)。这就需要一个读写锁来解决这个问题。在java5中java.util.concurrent包中已经包含读写锁;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c628f105a4eead5224f1076e66487750.html