1. Lock

并发包中的锁被用来控制多个线程访问共享资源的方式,于Java SE 5后加入,可以显式获取和释放锁(synchrozined则是隐式的)。

标准使用样例:

  1. Lock lock = new ReentrantLock();
  2. lock.lock();
  3. // Do Somthing
  4. lock.unlock(); // 要注意避免Do Something中发生异常而无法unlock()的死锁发生情况,建议把unlock写在finally里。

synchronized 不同的是:

  1. 非阻塞地获取锁: tryLock() 能获取锁到则返回 true 否则返回 false ,不强制进行阻塞。
  2. 能被中断地获取锁: lockInterruptibly() 在获取锁地过程中可以响应中断请求。
  3. 超时获取锁:规定时间内未获取到锁会直接放弃并返回false—— tryLock(long time...)
  4. 分组唤醒: newCondition() 可以利用等待通知组建来分组唤醒响应地线程。

    2. 队列同步器(AQS)

队列同步器AQS本质是一个CLH队列,主要用于实现锁。

  1. 具有三个共享状态访问方法: getStatesetState(int newState)compareAndSetState(int expect int update)
  2. 可重写的方法: | 方法名称 | 描述 | | —- | —- | | protected boolean tryAquire(int arg) | 独占式获取同步状态 | | protected boolean tryRelease(int arg) | 独占式释放同步状态 | | protected boolean tryAquireShared(int arg) | 共享式获取同步状态 | | protected boolean tryReleaseShared(int arg) | 共享式释放同步状态 | | protected boolean isHeldExclusively() | 判断当前队列同步器是否在独占模式下被线程所占用 |

  3. 在交由AQS的实现类代理时,需调用以下模板方法,即 lock() -> acquire() -> tryAcquire()aquire(int)aquireShared(int)aquireInterruptibly(int)aquireSharedInterruptibly(int)tryAquireNanos(int, long)tryAquireSharedNanos(int, long)release(int)releaseShared(int)

  4. 可通过 Collection<Thread> getQueuedThreads() 获取等待在同步队列上的线程集合。

  • 在运作时,没有获得同步状态的节点会被加入一个FIFO双向队列的尾部。这一过程需要基于CAS进行设置: compareAndSetTail() ,而设置头节点时则不需要。

  • 节点进入同步队列之后,就进入了自旋的过程,自旋过程将会阻塞节点对应的线程。自旋过程将会检定两件事:

  1. 前驱节点是否为头节点;
  2. 若为头节点,才尝试获取状态;

其好处为:节省(独占式)获取状态所需的开支、保证FIFO原则不被破坏。

3. 重入锁、读写锁、LockSupport、Condition接口

重入锁 ReentrantLock(boolean fair=false) 支持一个线程对已持有的资源进行重复加锁,并能构建公平锁和非公平锁。重入锁可以避免自己阻塞自己的情况,为显式重入( synchronized 是隐式重入)。

  • 公平锁:公平锁就是保障了多线程下各线程获取锁的顺序(FIFO)、绝对时间上一定可以获取到锁。
  • 非公平锁:非公平锁有饥饿现象,效率比公平锁高(cpu分时、无上下文切换开销)

读写锁 ReentrantReadWriteLock(boolean fair=false) 同一时刻允许多个读线程访问,但写线程访问并取得锁之后,其它线程均被阻塞。其特性有:可重进入、写锁可以被降为读锁。

  • 写锁可以被降为读锁被称为锁降级:保持写锁并获得读锁,最终放开写锁。样例代码如: ```java readLock.lock(); // 首先获取读锁,保证对条件状态(数据)的获取 if(!条件状态){ readLock.unlock(); // 释放读锁 writeLock.lock(); // 获取写锁,准备开始降级 try{
    1. if(!条件状态){
    2. 更新条件状态 = true; // 基于写锁更新条件状态(数据)
    3. }
    4. readLock.lock(); // 降级到读锁
    } finally{
    1. writeLock.unlock(); // 释放写锁
    } } // 至此,降级完成

try{ // Do Somthing with 条件状态(数据) } finally{ readLock.unlock(); }

  1. ---
  2. `LockSupport` 工具类用于阻塞或唤醒一个线程。阻塞: `park()` `parkNanos(long)` `parkUntil(long)` ;唤醒: `unpark(Thread)`
  3. ```java
  4. public class test { // 样例:多线程无限按序打印1~n的数字
  5. private static int n = 10;
  6. private static LinkedList<Thread> list = new LinkedList<>();
  7. public static void main(String[] args){
  8. for(int i=0; i<n; i++){
  9. Thread crn = new Thread(new R(i));
  10. list.add(crn);
  11. crn.start();
  12. }
  13. LockSupport.unpark(list.get(0));
  14. }
  15. static class R implements Runnable{
  16. private int id;
  17. R(int id){this.id = id;}
  18. @Override
  19. public void run(){
  20. while (true){
  21. LockSupport.park();
  22. System.out.println(this.id);
  23. LockSupport.unpark(list.get((this.id+ 1) % n)); // 唤醒下一个线程
  24. }
  25. }
  26. }
  27. }

Conditionwait()/notify() 的区别:

对比项目 Monitor Condition
等待队列个数 一个 多个
等待锁的同时响应中断 N Yes
当前线程释放锁并等待状态到未来某个时间 N Yes
是否公平 ? Yes

使用示例:

  1. ReentrantLock lock = new ReentrantLock();