Concurrent Program-03:Wait/Notify_线程状态_多把锁_活跃态_ReentranLock

一、 Wait/Notify

1. 原理和API

在这里插入图片描述在这里插入图片描述
wait() 方法会释放对象的锁,进入 WaitSet 等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify 为止
wait(long n) 有时限的等待, 到 n 毫秒后结束等待继续向下执行,或是被 notify

(1) 进入Object监视器的线程才能调用wait()方法:

@Slf4j(topic = "c.Test18")
public class Test18 {
    static final Object lock = new Object();
    
    public static void main(String[] args) {
        //只有成为了owner(获得了锁对象)才能调用wait()方法
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(2) 使用notify()唤醒等待区的一个线程 或 notifyAll()唤醒等待区所有的线程:

@Slf4j(topic = "c.TestWaitNotify")
public class TestWaitNotify {
    final static Object obj = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //被唤醒之后继续执行这个代码
                log.debug("其它代码....");
            }
        },"t1").start();

        new Thread(() -> {
            synchronized (obj) {
                log.debug("执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //被唤醒之后继续执行这个代码
                log.debug("其它代码....");
            }
        },"t2").start();

        // 主线程两秒后执行
        sleep(2);
        log.debug("唤醒 obj 上其它线程");
        synchronized (obj) {
//            obj.notify(); // 唤醒obj上一个线程
            obj.notifyAll(); // 唤醒obj上所有等待线程
        }
    }
}

notifyAll()的执行结果:

20:41:39.046 c.TestWaitNotify [t2] - 执行....
20:41:39.080 c.TestWaitNotify [t1] - 执行....
20:41:41.037 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
20:41:41.037 c.TestWaitNotify [t1] - 其它代码....
20:41:41.037 c.TestWaitNotify [t2] - 其它代码....

notify()的执行结果:

20:42:39.865 c.TestWaitNotify [t1] - 执行....
20:42:39.874 c.TestWaitNotify [t2] - 执行....
20:42:41.863 c.TestWaitNotify [main] - 唤醒 obj 上其它线程
20:42:41.864 c.TestWaitNotify [t1] - 其它代码....

2. wait/notify的使用

1、wait(n)与sleep(n)的区别

(1) 开始之前先看看sleep(long n) 和 wait(long n) 的区别:

  1. sleep 是 Thread 方法,而 wait 是 Object 的方法
  2. sleep 不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
  3. sleep 在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
  4. 线程状态相同: TIMED_WAITING
@Slf4j(topic = "c.Test19")
public class Test19 {
    static final Object lock = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (lock) {
                log.debug("获得锁");
                try {
                    Thread.sleep(20000);
                  //  lock.wait(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
        
        Sleeper.sleep(1);
        //主线程想要获取lock锁
        synchronized (lock) {
            log.debug("获得锁");
        }
    }
}

执行sleep()方法后的结果:在线程t1睡眠期间,主线程没有获得锁

20:57:34.663 c.Test19 [t1] - 获得锁

执行wait()方法后的结果:线程t1等待20s,在线程t1等待期间,主线程获得了锁

21:00:37.399 c.Test19 [t1] - 获得锁
21:00:38.397 c.Test19 [main] - 获得锁
2、使用过程

step1:

扫描二维码关注公众号,回复: 10036471 查看本文章
@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep1 {
    static final Object room = new Object();
    static boolean hasCigarette = false; // 有没有烟
    static boolean hasTakeout = false;

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                if (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    sleep(2);
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                }
            }
        }, "小南").start();

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                synchronized (room) {
                    log.debug("可以开始干活了");
                }
            }, "其它人").start();
        }

        sleep(1);
        new Thread(() -> {
            // 这里能不能加 synchronized (room)?
//            synchronized (room) {
                  hasCigarette = true;
//                log.debug("烟到了噢!");
//            }
        }, "送烟的").start();
    }
}
21:19:26.338 c.TestCorrectPosture [小南] - 有烟没?[false]
21:19:26.345 c.TestCorrectPosture [小南] - 没烟,先歇会!
21:19:28.345 c.TestCorrectPosture [小南] - 有烟没?[true]
21:19:28.345 c.TestCorrectPosture [小南] - 可以开始干活了
21:19:28.346 c.TestCorrectPosture [其它人] - 可以开始干活了
21:19:28.347 c.TestCorrectPosture [其它人] - 可以开始干活了
21:19:28.348 c.TestCorrectPosture [其它人] - 可以开始干活了
21:19:28.348 c.TestCorrectPosture [其它人] - 可以开始干活了
21:19:28.349 c.TestCorrectPosture [其它人] - 可以开始干活了

使用sleep()方法的缺陷:
在这里插入图片描述step2:

@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep2 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                if (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait();
                        //如果使用interrupt()方法打断等待中的线程会抛出线程,捕获异常后可以继续向下执行
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                }
            }
        }, "小南").start();

        //其他人对应的线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                synchronized (room) {
                    log.debug("可以开始干活了");
                }
            }, "其它人").start();
        }

        sleep(1);
        new Thread(() -> {
            synchronized (room) {
                hasCigarette = true;
                log.debug("烟到了噢!");
                //notify()方法也必须放在synchronized中,因为它也需要先拥有锁对象,才能执行
                room.notify();
            }
        }, "送烟的").start();
    }
}
21:27:50.830 c.TestCorrectPosture [其它人] - 可以开始干活了
21:27:50.835 c.TestCorrectPosture [其它人] - 可以开始干活了
21:27:50.836 c.TestCorrectPosture [小南] - 有烟没?[false]
21:27:50.841 c.TestCorrectPosture [小南] - 没烟,先歇会!
21:27:50.846 c.TestCorrectPosture [其它人] - 可以开始干活了
21:27:50.849 c.TestCorrectPosture [其它人] - 可以开始干活了
21:27:50.849 c.TestCorrectPosture [其它人] - 可以开始干活了
21:27:51.823 c.TestCorrectPosture [送烟的] - 烟到了噢!
21:27:51.824 c.TestCorrectPosture [小南] - 有烟没?[true]
21:27:51.824 c.TestCorrectPosture [小南] - 可以开始干活了

解决了其他线程阻塞问题,但是如果有其他线程也在等待呢?就是说等待的线程不止小南一个,那么会不会唤醒错了呢?

step3:

@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep3 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    // 虚假唤醒
    public static void main(String[] args) {
        //小南线程等待烟
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                if (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小南").start();

        //小女线程等待外卖
        new Thread(() -> {
            synchronized (room) {
                Thread thread = Thread.currentThread();
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (hasTakeout) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小女").start();

        sleep(1);
        new Thread(() -> {
            synchronized (room) {
                //外卖到了
                hasTakeout = true;
                log.debug("外卖到了噢!");
                //随机从等待区叫醒一个线程
                room.notify();
            }
        }, "送外卖的").start();
    }
}

送外卖的应该叫醒小南但是却把小女叫醒了:

21:36:20.549 c.TestCorrectPosture [小南] - 有烟没?[false]
21:36:20.555 c.TestCorrectPosture [小南] - 没烟,先歇会!
21:36:20.555 c.TestCorrectPosture [小女] - 外卖送到没?[false]
21:36:20.555 c.TestCorrectPosture [小女] - 没外卖,先歇会!
21:36:21.549 c.TestCorrectPosture [送外卖的] - 外卖到了噢!
21:36:21.550 c.TestCorrectPosture [小南] - 有烟没?[false]
21:36:21.550 c.TestCorrectPosture [小南] - 没干成活...

notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线程,称之为【虚假唤醒】。

step4: 解决方法:改为 notifyAll

21:56:42.864 c.TestCorrectPosture [小南] - 有烟没?[false]
21:56:42.898 c.TestCorrectPosture [小南] - 没烟,先歇会!
21:56:42.899 c.TestCorrectPosture [小女] - 外卖送到没?[false]
21:56:42.900 c.TestCorrectPosture [小女] - 没外卖,先歇会!
21:56:43.850 c.TestCorrectPosture [送外卖的] - 外卖到了噢!
21:56:43.851 c.TestCorrectPosture [小女] - 外卖送到没?[true]
21:56:43.851 c.TestCorrectPosture [小女] - 可以开始干活了
21:56:43.852 c.TestCorrectPosture [小南] - 有烟没?[false]
21:56:43.852 c.TestCorrectPosture [小南] - 没干成活...

从结果可以看出小南干成活,小女干成活了:
用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新判断的机会了

step5: 解决方法,用 while + wait,当条件不成立,再次 wait,什么时候条件成立了才往下走,就是说只要送的不是烟而是外卖我就继续等待

@Slf4j(topic = "c.TestCorrectPosture")
public class TestCorrectPostureStep5 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (room) {
                log.debug("有烟没?[{}]", hasCigarette);
                //条件不成立,就会进行下一轮的等待,而不会向下执行
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("有烟没?[{}]", hasCigarette);
                if (hasCigarette) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小南").start();

        new Thread(() -> {
            synchronized (room) {
                Thread thread = Thread.currentThread();
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        room.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("外卖送到没?[{}]", hasTakeout);
                if (hasTakeout) {
                    log.debug("可以开始干活了");
                } else {
                    log.debug("没干成活...");
                }
            }
        }, "小女").start();

        sleep(1);
        new Thread(() -> {
            synchronized (room) {
                hasTakeout = true;
                log.debug("外卖到了噢!");
                room.notifyAll();
            }
        }, "送外卖的").start();
    }
}
22:05:09.560 c.TestCorrectPosture [小南] - 有烟没?[false]
22:05:09.578 c.TestCorrectPosture [小南] - 没烟,先歇会!
22:05:09.578 c.TestCorrectPosture [小女] - 外卖送到没?[false]
22:05:09.578 c.TestCorrectPosture [小女] - 没外卖,先歇会!
22:05:10.562 c.TestCorrectPosture [送外卖的] - 外卖到了噢!
22:05:10.563 c.TestCorrectPosture [小女] - 外卖送到没?[true]
22:05:10.563 c.TestCorrectPosture [小女] - 可以开始干活了
22:05:10.564 c.TestCorrectPosture [小南] - 没烟,先歇会!

使用wait / notify总结:
在这里插入图片描述

3. 保护性暂停模式

在这里插入图片描述

4. 生产者消费者模式

在这里插入图片描述

@Slf4j(topic = "c.Test21")
public class Test21 {

    public static void main(String[] args) {
        MessageQueue queue = new MessageQueue(2);

        for (int i = 0; i < 3; i++) {
            int id = i;
            new Thread(() -> {
                queue.put(new Message(id , "值"+id));
            }, "生产者" + i).start();
        }

        new Thread(() -> {
            while(true) {
                sleep(1);
                Message message = queue.take();
            }
        }, "消费者").start();
    }

}

// 消息队列类 , java 线程之间通信
@Slf4j(topic = "c.MessageQueue")
class MessageQueue {
    // 消息的队列集合,双向链表
    private LinkedList<Message> list = new LinkedList<>();

    // 队列容量
    private int capcity;

    public MessageQueue(int capcity) {
        this.capcity = capcity;
    }

    // 获取消息
    public Message take() {
        // 检查队列是否为空
        synchronized (list) {
            //唤醒后判断队列不为空,就会向下执行取消息
            while(list.isEmpty()) {
                try {
                    log.debug("队列为空, 消费者线程等待");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 从队列头部获取消息并返回
            Message message = list.removeFirst();
            log.debug("已消费消息 {}", message);
            list.notifyAll();
            return message;
        }
    }

    // 存入消息
    public void put(Message message) {
        synchronized (list) {
            // 检查对象是否已满
            //唤醒后判断队列没满就会继续存消息
            while(list.size() == capcity) {
                try {
                    log.debug("队列已满, 生产者线程等待");
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 将消息加入队列尾部
            list.addLast(message);
            log.debug("已生产消息 {}", message);
            //让等待中的线程都唤醒
            list.notifyAll();
        }
    }
}

@Data
@Getter
@AllArgsConstructor
final class Message {
    private int id;
    private Object value;
}

结果:

22:51:18.539 c.MessageQueue [生产者0] - 已生产消息 Message(id=0, value=0)
22:51:18.561 c.MessageQueue [生产者1] - 已生产消息 Message(id=1, value=1)
22:51:18.562 c.MessageQueue [生产者2] - 队列已满, 生产者线程等待
22:51:19.549 c.MessageQueue [消费者] - 已消费消息 Message(id=0, value=0)
22:51:19.549 c.MessageQueue [生产者2] - 已生产消息 Message(id=2, value=2)
22:51:20.549 c.MessageQueue [消费者] - 已消费消息 Message(id=1, value=1)
22:51:21.549 c.MessageQueue [消费者] - 已消费消息 Message(id=2, value=2)
22:51:22.550 c.MessageQueue [消费者] - 队列为空, 消费者线程等待

二、park / unpark

在这里插入图片描述

public class TestParkUnpark {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("start...");
            sleep(2);
            log.debug("park...");

            LockSupport.park();
            log.debug("resume...");
        }, "t1");
        t1.start();

        sleep(1);
        log.debug("unpark...");
        LockSupport.unpark(t1);
    }
}

结果:

22:56:48.547 c.TestParkUnpark [t1] - start...
22:56:49.537 c.TestParkUnpark [main] - unpark...
22:56:50.648 c.TestParkUnpark [t1] - park...
22:56:50.648 c.TestParkUnpark [t1] - resume...

与 Object 的 wait & notify 相比:

  • park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
  • wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
  • park & unpark 可以先 unpark,而 wait & notify 不能先 notify

三、 线程状态装换

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

四、多把锁

看下面的一个线程代码:一间大屋子有两个功能:睡觉、学习,互不相干。
现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小南").start();
        new Thread(() -> {
            bigRoom.sleep();
        },"小女").start();
    }
}

@Slf4j(topic = "c.BigRoom")
class BigRoom {
    public void sleep() {
        synchronized (this) {
            log.debug("sleeping 2 小时");
            Sleeper.sleep(2);
        }
    }

    public void study() {
        synchronized (this) {
            log.debug("study 1 小时");
            Sleeper.sleep(1);
        }
    }
}

当小南睡觉的时候占有着锁,小女就不能学习,解决的方法是使用多把锁:

public class TestMultiLock {
    public static void main(String[] args) {
        BigRoom bigRoom = new BigRoom();
        new Thread(() -> {
            bigRoom.study();
        },"小南").start();
        new Thread(() -> {
            bigRoom.sleep();
        },"小女").start();
    }
}

@Slf4j(topic = "c.BigRoom")
class BigRoom {

    private final Object studyRoom = new Object();

    private final Object bedRoom = new Object();

    public void sleep() {
        synchronized (bedRoom) {
            log.debug("sleeping 2 小时");
            Sleeper.sleep(2);
        }
    }

    public void study() {
        synchronized (studyRoom) {
            log.debug("study 1 小时");
            Sleeper.sleep(1);
        }
    }
}

将锁的粒度细分:
 好处,是可以增强并发度
 坏处,如果一个线程需要同时获得多把锁,就容易发生死锁

五、线程活跃性

死锁:

有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁
t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁

@Slf4j(topic = "c.TestDeadLock")
public class TestDeadLock {
    public static void main(String[] args) {
        test1();
    }

    private static void test1() {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                log.debug("lock A");
                sleep(1);
                synchronized (B) {
                    log.debug("lock B");
                    log.debug("操作...");
                }
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            synchronized (B) {
                log.debug("lock B");
                sleep(0.5);
                synchronized (A) {
                    log.debug("lock A");
                    log.debug("操作...");
                }
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

活锁:

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如

@Slf4j(topic = "c.TestLiveLock")
public class TestLiveLock {
    static volatile int count = 10;
    static final Object lock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            // 期望减到 0 退出循环
            while (count > 0) {
                sleep(0.2);
                count--;
                log.debug("count: {}", count);
            }
        }, "t1").start();
        new Thread(() -> {
            // 期望超过 20 退出循环
            while (count < 20) {
                sleep(0.2);
                count++;
                log.debug("count: {}", count);
            }
        }, "t2").start();
    }
}

六、ReentrantLock

synchronized与ReentrantLock的区别:
在这里插入图片描述

1. 可重入

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住

public class hh {

    //创建锁对象
    static ReentrantLock lock = new ReentrantLock();
    public static void main(String[] args) {
        method1();
    }
    public static void method1() {
        //给m1方法加了锁
        lock.lock();
        try {
            log.debug("execute method1");
            //m1还没解锁,又去调用m2
            method2();
        } finally {
            lock.unlock();
        }
    }
    public static void method2() {
        //给m2加锁
        lock.lock();
        try {
            log.debug("execute method2");
            method3();
        } finally {
            lock.unlock();
        }
    }
    public static void method3() {
        lock.lock();
        try {
            log.debug("execute method3");
        } finally {
            lock.unlock();
        }
    }
}

2. 可打断

可以避免无限制的等待锁,被动的避免死等:lock.lockInterruptibly()

public class hh {
    ReentrantLock lock = new ReentrantLock();
    Thread t1 = new Thread(() -> {
        log.debug("启动...");
        try {
            //可打断锁
            //如果没有竞争,此方法就会获的Lock对象锁
            //如果有竞争进入阻塞队列等待,可以被其他线程的interrupt方法打断,打断后不再等待直接进入catch语句块
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.debug("等锁的过程中被打断");
            return;
        }
        
        try {
            log.debug("获得了锁");
        } finally {
            lock.unlock();
        }
    }, "t1");

    //主线程先获了锁,t1线程就会进入阻塞队列进行等待
    lock.lock();
    log.debug("获得了锁");
    
    t1.start();
    
    try{
        //主线程睡眠
        sleep(1);
        //t1线程等待时,可以打断,让其不再等待,直接向下指向catch语句块,不再获取锁
        t1.interrupt();
    } finally{
        lock.unlock();
    }
}

在这里插入图片描述注意如果是不可中断模式,那么即使使用了 interrupt 也不会让等待中断:lock.lock()

public class hh {
    ReentrantLock lock = new ReentrantLock();
    Thread t1 = new Thread(() -> {
        log.debug("启动...");
        try {
            //注意如果是不可中断模式,那么即使使用了interrupt也不会让等待中断
            lock.lock();
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.debug("等锁的过程中被打断");
            return;
        }

        try {
            log.debug("获得了锁");
        } finally {
            lock.unlock();
        }
    }, "t1");

    lock.lock();
    log.debug("获得了锁");

    t1.start();

    try{
        //主线程睡眠
        sleep(1);
        t1.interrupt();
    } finally{
        lock.unlock();
    }
}

在这里插入图片描述

3. 锁超时

lock.tryLock()
lock.tryLock(2, TimeUnit.SECONDS)

@Slf4j(topic = "c.Test22")
public class Test22 {
    //创建锁对象
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("尝试获得锁");
            try {
                //尝试获得锁,成功为true,失败为false
                //如果为false,不用向下执行
                //等待2s
                if (! lock.tryLock(2, TimeUnit.SECONDS)) {
                    log.debug("获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("获取不到锁");
                return;
            }
            
            //如果为true
            try {
                log.debug("获得到锁");
            } finally {
                lock.unlock();
            }
        }, "t1");
        
        //主线程一直持有锁
        lock.lock();
        log.debug("获得到锁");
        t1.start();
        
        //主线程1s后释放了锁,t1线程等待2s,在等待时间内获得了锁
        sleep(1);
        log.debug("释放了锁");
        lock.unlock();
    }
}

ReentrantLock 默认是不公平的,主要是解决饥饿问题,但是没有必要设置公平锁,因为会降低并发度。

4. 条件变量

在这里插入图片描述

@Slf4j(topic = "c.Test24")
public class Test24 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;
    static ReentrantLock ROOM = new ReentrantLock();
    // 等待烟的休息室
    static Condition waitCigaretteSet = ROOM.newCondition();
    // 等外卖的休息室
    static Condition waitTakeoutSet = ROOM.newCondition();

    public static void main(String[] args) {

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("有烟没?[{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        waitCigaretteSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        waitTakeoutSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小女").start();

        sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeout = true;
                waitTakeoutSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送外卖的").start();

        sleep(1);

        new Thread(() -> {
            ROOM.lock();
            try {
                hasCigarette = true;
                waitCigaretteSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送烟的").start();
    }
}
10:16:47.228 c.Test24 [小女] - 外卖送到没?[false]
10:16:47.247 c.Test24 [小女] - 没外卖,先歇会!
10:16:47.248 c.Test24 [小南] - 有烟没?[false]
10:16:47.248 c.Test24 [小南] - 没烟,先歇会!
10:16:48.200 c.Test24 [小女] - 可以开始干活了
10:16:49.241 c.Test24 [小南] - 可以开始干活了
发布了665 篇原创文章 · 获赞 115 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_42764468/article/details/104997772