JUC 底层支持 LockSupport

线程等待和唤醒三种方式

方式 等待 唤醒 描述
Object wait notity/notifyAll 必须用在 synchronized 里,需要先等待再唤醒
Condition await singal 必须用在 Lock 块中,需要先等待再唤醒
LockSupport park unpark 无限制

wait/notify

public static void main(String[] args) throws InterruptedException  {
    Object obj = new Object();

    // 线程 A 要先等待,且必须用在 synchronized 中
    new Thread(() -> {
        synchronized (obj) {
            System.out.println("线程A come in...");
            try {
                obj.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("线程A 被唤醒");
        }
    }, "线程A").start();
    
    TimeUnit.SECONDS.sleep(1);

    // 唤醒的线程必须已经处理等待中,先唤醒后等待就不行,也必须用在 synchronized 中
    new Thread(() -> {
        synchronized (obj) {
            obj.notify();
            System.out.println("线程B 发出通知");
        }
    }, "线程B").start();
}

await/singal

public static void main(String[] args) throws InterruptedException {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    // 线程 A 要先等待,且必须用在 lock 代码块中
    new Thread(() -> {
        lock.lock();
        System.out.println("线程A come in...");
        try {
            condition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }, "线程A").start();

    TimeUnit.SECONDS.sleep(1);

    // 唤醒的线程必须已经处理等待中,先唤醒后等待就不行,也必须用在 lock 代码块中
    new Thread(() -> {
        lock.lock();
        try {
            condition.signal();
            System.out.println("线程B 发出通知");
        } finally {
            lock.unlock();
        }
    }, "线程B").start();
}

park/unpark

public static void main(String[] args) throws InterruptedException {

    Thread t1 = new Thread(() -> {
        TimeUnit.SECONDS.sleep(2); // 线程 A 休眠,让线程 B 先执行(模拟先唤醒后等待)
        System.out.println("线程A come in...");
        LockSupport.park();
        System.out.println("线程A 被唤醒");
    }, "线程A");
    t1.start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        System.out.println("线程B 发出通知");
        LockSupport.unpark(t1);
    }, "线程B").start();
}

LockSupport 常用方法

方法 解释 备注
park() 阻塞线程 当前线程没有令牌就一直阻塞
parkNanos(long nanos) 阻塞线程多少纳秒 时间结束或有令牌将唤醒(相对时间)
parkUntil(long deadline) 阻塞线程到具体时间 到了时间或有令牌将唤醒(绝对时间)
unpark(Thread t) 给指定线程颁发令牌 最多持有一个令牌

各种阻塞方法区别

  • Thread.sleep() 和 Object.wait()

    • sleep 不能外部唤醒,只能自己醒过来,wait 既可以外部唤醒,也可以自己醒过来
    • 最大的区别就是 sleep 不会释放锁,wait 会释放锁
  • Object.wait() 和 Condition.await():

    • Object.wait() 是个 native 方法, jvm 实现;Condition.await() JDK 实现,底层调用 LockSupport.park() 实现
    • Condition.await() 先把线程加入 AQS 的条件队列,然后把 AQS 的 state -1,最后才是调用 LockSupport.park() 阻塞队列
  • Thread.sleep() 和 LockSupport.park()

    LockSupport.park() 还有几个兄弟方法:parkNanos()、parkUtil()

    • sleep 不能外部唤醒,只能自己醒过来,LockSupport.park() 可以自己醒过来也可以外部唤醒
    • sleep 是 native 方法,LockSupport.park() 底层调了 Unsafe 的 native 方法
    • 两者都不会释放锁
  • Object.wait() 和 LockSupport.park()

    • Object.wait() 方法需要用在 synchronized 块中,LockSupport.park() 无限制
    • Object.wait() 不带超时的,需要另一个线程执行 notify() 来唤醒,但不一定继续执行后续内容
    • LockSupport.park() 不带超时的,需要另一个线程执行 unpark() 来唤醒,一定会继续执行后续内容
posted @ 2023-05-24 15:59  CyrusHuang  阅读(19)  评论(0编辑  收藏  举报