java等待唤醒机制进化之LockSupport

1. Object类中的wait和notify方法实现线程等待和唤醒

private static void waitNotify() {
        Object obj = new Object();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {

            }
            synchronized (obj) {
                try {
                    System.out.println(Thread.currentThread().getName() + "启动了");
                    obj.wait();
                    System.out.println(Thread.currentThread().getName()+"被唤醒");
                } catch (InterruptedException e) {

                }
            }
        }, "线程一").start();
        new Thread(() -> {
            synchronized (obj) {
                System.out.println(Thread.currentThread().getName() + "启动了");
                obj.notify();
                System.out.println(Thread.currentThread().getName()+"开始唤醒其它线程");
            }
        }, "线程二").start();
    }

限制:

wait和notify方法必须要在同步块或者方法里面且成对出现使用,否则会抛出java.lang.IllegalMonitorStateException。

调用顺序要先wait后notify才OK。

2. Condition接口中的await后signal方法实现线程的等待和唤醒,与Object类中的wait和notify方法实现线程等待和唤醒类似。

private static void awaitSignal() {
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {

            }
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "启动了");
            try {
                condition.await();
            } catch (InterruptedException e) {

            }
            System.out.println(Thread.currentThread().getName()+"被唤醒了");
            lock.unlock();
        },"t1").start();

        new Thread(()->{
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "启动了");

            condition.signal();
            System.out.println(Thread.currentThread().getName()+"开始唤醒其它线程");
            lock.unlock();
        },"t2").start();
    }

await和signal方法必须要在同步块或者方法里面且成对出现使用,否则会抛出java.lang.IllegalMonitorStateException。

调用顺序要先await后signal才OK。

3. LockSupport类中的park等待和unpark唤醒

 public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "启动了");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "被唤醒了");
        }, "t1");
        t1.start();

        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "启动了");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {

            }
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "开始唤醒其它指定线程");
        }, "t2");
        t2.start();
    }

LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个许可(permit),permit只有两个值1和零,默认是零。可以把许可看成是一种(0.1)信号量(Semaphore),但与Semaphore不同的是,许可的累加上限是1。permit默认是0,所以一开始调用park()方法,当前线程就会阻塞,直到别的线程将当前线程的permit设置为1时,park方法会被唤醒,然后会将permit再次设置为0并返回。调用unpark(thread)方法后,就会将thread线程的许可permit设置成1(注意多次调用unpark方法,不会累加,pemit值还是1)会自动唤醒thead线程,即之前阻塞中的LockSupport.park()方法会立即返回。

优势:不用加锁,而且可以等待和唤醒的顺序可以任意。

原文地址:https://www.cnblogs.com/yerikm/p/15340425.html