目录
2.2 Object类中的wait和notify方法实现线程等待和唤醒
2.2.2 如果去除synchronized的代码,注释掉以后
2.2.3 先执行notify,后执行wait是否可以?测试
2.3 Condition接口中await后signal方法实现线程的等待和唤醒
2.4 传统的synchronized和Lock方法实现线程的等待唤醒通知的约束
2.5 LockSupport类中的park等待和unpark唤醒
1 是什么
2 线程等待唤醒机制(wait/notify)
2.1 3种让线程等待和唤醒的方法
1.使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
2.使用JUC保重的Condition的await方法让线程等待 ,使用signal()方法唤醒线程
3.LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程。
2.2 Object类中的wait和notify方法实现线程等待和唤醒
2.2.1 DEMO-wait/notify最原始的案例
public class LockSupportDemo {
static Object objectLock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (objectLock) {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
}
}, "A").start();
new Thread(() -> {
synchronized (objectLock) {
objectLock.notify();
System.out.println(Thread.currentThread().getName() + "\t -----通知");
}
}, "B").start();
}
}
输出
A -----come in
B -----通知
A -----被唤醒
2.2.2 如果去除synchronized的代码,注释掉以后
// synchronized (objectLock) {
wait/notify
//}
输出:
A -----come in
Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.demo.a18_lock.LockSupportDemo.lambda$main$0(LockSupportDemo.java:17)
at java.lang.Thread.run(Thread.java:748)
java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.demo.a18_lock.LockSupportDemo.lambda$main$1(LockSupportDemo.java:28)
at java.lang.Thread.run(Thread.java:748)
wait和notify必须要在同步代码块或者方法里面且成对出现使用
2.2.3 先执行notify,后执行wait是否可以?测试
在A线程中增加睡眠
new Thread(() -> {
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectLock) {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
try {
objectLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
}
}, "A").start();
输出
B -----通知
A -----come in
且程序未结束
由于先notify,后wait,wait后没有任务唤醒线程,故被唤醒代码未执行,且程序一直处于wait状态。
故,先wait后notify才可以
2.3 Condition接口中await后signal方法实现线程的等待和唤醒
public class LockSupportDemo2 {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) {
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
try{
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
} finally {
lock.unlock();
}
}, "A").start();
new Thread(() -> {
lock.lock();
try {
condition.signal();
System.out.println(Thread.currentThread().getName() + "\t -----通知");
} finally {
lock.unlock();
}
}, "B").start();
}
}
输出
A -----come in
B -----通知
A -----被唤醒
2.3.1 注释lock unlock方法
输出
await和signal也要配合 lock和unlock方法使用。
2.3.2 await和signal调换顺序
输出
和上面synchronized一样,顺序不可调整。
2.4 传统的synchronized和Lock方法实现线程的等待唤醒通知的约束
线程先要获得并持有锁,必须在锁块(synchronized或lock)中。
必须要先等待后唤醒,线程才能够被唤醒。
2.5 LockSupport类中的park等待和unpark唤醒
2.5.1 是什么
通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作。
2.5.2 主要方法
API:
阻塞:
唤醒:
2.5.3 DEMO
无所块要求:
public class LockSupportDemo3 {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
LockSupport.park();//被阻塞...等待通知,等待放行
System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
}, "a");
a.start();
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
Thread b = new Thread(() -> {
LockSupport.unpark(a);//被阻塞...等待通知,等待放行
System.out.println(Thread.currentThread().getName() + "\t -----通知");
}, "b");
b.start();
}
}
改成先唤醒后等待,测试
可以看出,LockSupport没有报错,unpark可以在park之前执行, 只是park无效,没有阻塞效果,可以看到输出中时间戳一致,park形同虚设
2.5.4 底层代码解析
2.5.6 面试题
3 参考文献
以上内容均来自下方的视频,仅用于个人学习笔记记录使用