Java线程等待唤醒的三种方法

线程等待唤醒的三种方法

需求:我们实现A线程等待B线程执行完在执行。

Object下面的wait()和notify()

使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程

public static void main(String[] args) throws InterruptedException {
    Object o = new Object();
    new Thread(() -> {
        synchronized (o) {
            log.info("准备执行A");
            try {
                o.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("A执行完成");
        }
    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        synchronized (o) {
            log.info("我是B,我要唤醒A");
            o.notify();
        }
    }, "B").start();
}

运行:

15:36:43.390 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
15:36:44.395 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
15:36:44.395 [A] INFO com.yougong.digitalwallet.controller.TestTest - A执行完成
    

思考1

  • 如果去掉 synchronized,再次执行
public static void main(String[] args) throws InterruptedException {
    Object o = new Object();
    new Thread(() -> {
        //synchronized (o) {
        log.info("准备执行A");
        try {
            o.wait();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("A执行完成");
        //}
    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        //synchronized (o) {
        log.info("我是B,我要唤醒A");
        o.notify();
        //}
    }, "B").start();
}

结果:

15:38:58.016 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
Exception in thread "A" java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.yougong.digitalwallet.controller.TestTest.lambda$main$0(TestTest.java:22)
	at java.lang.Thread.run(Thread.java:748)
15:38:59.029 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.yougong.digitalwallet.controller.TestTest.lambda$main$1(TestTest.java:35)
	at java.lang.Thread.run(Thread.java:748)

思考2

先 notify 再 wait 会怎么

public static void main(String[] args) throws InterruptedException {
    Object o = new Object();
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized (o) {
            log.info("准备执行A");
            try {
                o.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("A执行完成");
        }
    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        synchronized (o) {
            log.info("我是B,我要唤醒A");
            o.notify();
        }
    }, "B").start();
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NBMmUrmE-1688112916989)(C:\Users\86183\AppData\Roaming\Typora\typora-user-images\image-20230630154157434.png)]

发现:程序无法执行,无法唤醒

总结

  • wait和notify方法必须要在同步块或者方法里面,且成对出现使用

  • 先wait后notify才OK

Condition类的await()和signal()方法

使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

public static void main(String[] args) throws InterruptedException {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    new Thread(() -> {
        log.info("准备执行A");
        lock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        log.info("A执行完成");

    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        lock.lock();
        try {
            log.info("我是B,我要唤醒A");
            condition.signal();
        } finally {
            lock.unlock();
        }
    }, "B").start();
}

运行:

15:45:28.053 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
15:45:29.056 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
15:45:29.056 [A] INFO com.yougong.digitalwallet.controller.TestTest - A执行完成

思考1

如果去掉 lock,再次执行

public static void main(String[] args) throws InterruptedException {
    //m1();
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    new Thread(() -> {
        log.info("准备执行A");
        //lock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //lock.unlock();
        }
        log.info("A执行完成");

    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        //lock.lock();
        try {
            log.info("我是B,我要唤醒A");
            condition.signal();
        } finally {
            //lock.unlock();
        }
    }, "B").start();
}

运行:

15:48:05.392 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
Exception in thread "A" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
	at com.yougong.digitalwallet.controller.TestTest.lambda$main$0(TestTest.java:28)
	at java.lang.Thread.run(Thread.java:748)
15:48:06.404 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
	at com.yougong.digitalwallet.controller.TestTest.lambda$main$1(TestTest.java:44)
	at java.lang.Thread.run(Thread.java:748)

思考2

先 signal 再 await 会怎么

代码:

public static void main(String[] args) throws InterruptedException {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("准备执行A");
        lock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
        log.info("A执行完成");

    }, "A").start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        lock.lock();
        try {
            log.info("我是B,我要唤醒A");
            condition.signal();
        } finally {
            lock.unlock();
        }
    }, "B").start();
}

运行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nS6JELET-1688112916990)(C:\Users\86183\AppData\Roaming\Typora\typora-user-images\image-20230630154901490.png)]

总结

  • await和single方法必须要在同步块或者方法里面,且成对出现使用

  • 先await后single才OK

LockSupport类的park()和unpark()方法

LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

LockSupport类使用的是一种名为 Permit(许可)的概念做到阻塞和唤醒线程的,每一个线程都有一个许可(Permit),permit只存在两个值0或者1,默认为0.

调用LockSupport.park()时,当前线程就会阻塞,因为permit默认为0,直到别的线程将当前线程的permit设置成1时,park方法才会被唤醒,然后会将permit设置为0,继续执行park后面的代码。

调用LockSupport的unpark(thread)方法时,会将thread线程的permit就会被设置成1,然后自动唤醒thread线程继续执行之前阻塞的park方法后面的代码。注意:unpark(thread)无论执行多少遍,permit的值只可能是1,不会依次累加。

代码:

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        log.info("准备执行A");
        LockSupport.park();
        log.info("A执行完成");
    }, "A");
    thread.start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        log.info("我是B,我要唤醒A");
        LockSupport.unpark(thread);
    }, "B").start();
}

运行:

16:12:03.318 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
16:12:04.320 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
16:12:04.320 [A] INFO com.yougong.digitalwallet.controller.TestTest - A执行完成

思考2

先 unpark 再 park会怎么

代码:

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        log.info("准备执行A");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        LockSupport.park();
        log.info("A执行完成");
    }, "A");
    thread.start();

    TimeUnit.SECONDS.sleep(1);

    new Thread(() -> {
        log.info("我是B,我要唤醒A");
        LockSupport.unpark(thread);
    }, "B").start();
}

运行:

16:12:57.447 [A] INFO com.yougong.digitalwallet.controller.TestTest - 准备执行A
16:12:58.453 [B] INFO com.yougong.digitalwallet.controller.TestTest - 我是B,我要唤醒A
16:13:00.450 [A] INFO com.yougong.digitalwallet.controller.TestTest - A执行完成

没问题。。。。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值