synchronized: 容易发生死锁。

    synchronized 申请资源的时候,如果申请不到,线程直接进入阻塞状态了,而线程进入阻塞状态,啥都干不了,也释放不了线程已经占有的资源。

    并发编程领域,有两大核心问题:

    1. 一个是互斥,即同一时刻只允许一个线程访问共享资源;
    2. 另一个是同步,即线程之间如何通信、协作。

    Lock 用于解决互斥问题,Condition 用于解决同步问题

    image.png

    image.png

    既然synchronized容易发生死锁,Condition和Lock可以防止死锁


    对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了

    image.png

    如果我们重新设计一把互斥锁去解决这个问题,那该怎么设计呢?我觉得有三种方案。

    1. 能够响应中断。synchronized 的问题是,持有锁 A 后,如果尝试获取锁 B 失败,那么线程就进入阻塞状态,一旦发生死锁,就没有任何机会来唤醒阻塞的线程。但如果阻塞状态的线程能够响应中断信号,也就是说当我们给阻塞的线程发送中断信号的时候,能够唤醒它,那它就有机会释放曾经持有的锁 A。这样就破坏了不可抢占条件了。
    2. 支持超时。如果线程在一段时间之内没有获取到锁,不是进入阻塞状态,而是返回一个错误,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。
    3. 非阻塞地获取锁。如果尝试获取锁失败,并不进入阻塞状态,而是直接返回,那这个线程也有机会释放曾经持有的锁。这样也能破坏不可抢占条件。

    lock接口的三种方法,对应三种解决方案

    1. // 支持中断的 API
    2. void lockInterruptibly() throws InterruptedException;
    3. // 支持超时的 API
    4. boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    5. // 支持非阻塞获取锁的 API
    6. boolean tryLock();


    1. class X {
    2. private final Lock rtl =
    3. new ReentrantLock();
    4. int value;
    5. public void addOne() {
    6. // 获取锁
    7. rtl.lock();
    8. try {
    9. value+=1;
    10. } finally {
    11. // 保证锁能释放
    12. rtl.unlock();
    13. }
    14. }
    15. }

    ReentrantLock,这个翻译过来叫可重入锁,这个概念前面我们一直没有介绍过。所谓可重入锁,顾名思义,指的是线程可以重复获取同一把锁

    image.png

    image.png

    在使用 ReentrantLock 的时候,你会发现 ReentrantLock 这个类有两个构造函数,一个是无参构造函数,一个是传入 fair 参数的构造函数。fair 参数代表的是锁的公平策略,如果传入 true 就表示需要构造一个公平锁,反之则表示要构造一个非公平锁。

    我们介绍过入口等待队列,锁都对应着一个等待队列,如果一个线程没有获得锁,就会进入等待队列,当有线程释放锁的时候,就需要从等待队列中唤醒一个等待的线程。如果是公平锁,唤醒的策略就是谁等待的时间长,就唤醒谁,很公平;如果是非公平锁,则不提供这个公平保证,有可能等待时间短的线程反而先被唤醒。

    等待通知实现并发线程之间的通信,同步。实现协同。
    在synchronized中通过wait,notify和notifyAll实现
    在lock/condition中通过await,signal和signalall实现

    1. public class BlockedQueue<T>{
    2. final Lock lock =
    3. new ReentrantLock();
    4. // 条件变量:队列不满
    5. final Condition notFull =
    6. lock.newCondition();
    7. // 条件变量:队列不空
    8. final Condition notEmpty =
    9. lock.newCondition();
    10. // 入队
    11. void enq(T x) {
    12. lock.lock();
    13. try {
    14. while (队列已满){
    15. // 等待队列不满
    16. notFull.await();
    17. }
    18. // 省略入队操作...
    19. // 入队后, 通知可出队
    20. notEmpty.signal();
    21. }finally {
    22. lock.unlock();
    23. }
    24. }
    25. // 出队
    26. void deq(){
    27. lock.lock();
    28. try {
    29. while (队列已空){
    30. // 等待队列不空
    31. notEmpty.await();
    32. }
    33. // 省略出队操作...
    34. // 出队后,通知可入队
    35. notFull.signal();
    36. }finally {
    37. lock.unlock();
    38. }
    39. }
    40. }