一、wait-notify/notifyAll
public class Study {
public static void main(String[] args) {
Object o = new Object();
new Thread(()->{
synchronized (o){
System.out.println("A即将被阻塞");
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A出来了");
}
},"A").start();
new Thread(()->{
synchronized (o){
System.out.println("B要唤醒A了");
o.notifyAll();
}
},"B").start();
}
}
从以上代码可见,先wait()后notify(),A最后被唤醒了
如果先notify()后wait()呢?
public class Study {
public static void main(String[] args) {
Object o = new Object();
new Thread(()->{
//先睡眠2秒
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o){
System.out.println("A即将被阻塞");
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A出来了");
}
},"A").start();
new Thread(()->{
synchronized (o){
System.out.println("B要唤醒A了");
o.notifyAll();
}
},"B").start();
}
}
可见A没有被唤醒
以上可总结出wait-notify的两个缺点?
①必须与synchronized一起用
②必须执行wait()方法的线程先执行,执行notify()方法的线程后执行;
二、await-signal
public class Study {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition =reentrantLock.newCondition();
new Thread(()->{
reentrantLock.lock();
try{
System.out.println("A要被阻塞了");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A被唤醒了");
}finally {
reentrantLock.unlock();
}
},"A").start();
new Thread(()->{
reentrantLock.lock();
try{
System.out.println("B要唤醒A了");
try {
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}
}finally {
reentrantLock.unlock();
}
},"B").start();
}
}
从以上代码可见,先await()后signal(),线程A被唤醒了
那么先signal()后await(),会是啥呢?
public class Study {
public static void main(String[] args) {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition =reentrantLock.newCondition();
new Thread(()->{
//睡眠2秒让执行signal()的线程先执行
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
reentrantLock.lock();
try{
System.out.println("A要被阻塞了");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A被唤醒了");
}finally {
reentrantLock.unlock();
}
},"A").start();
new Thread(()->{
reentrantLock.lock();
try{
System.out.println("B要唤醒A了");
try {
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}
}finally {
reentrantLock.unlock();
}
},"B").start();
}
}
以上可见 await-signal的缺点:
①要依赖与ReentrantLock锁
②signal先于await执行会导致阻塞
三.park-unpark
public class Study {
public static void main(String[] args) {
Thread A = new Thread(()-> {
System.out.println("A线程要被阻塞了");
LockSupport.park();
System.out.println("A线程出来了");
});
A.start();
new Thread(()->{
System.out.println("B要唤醒A了");
LockSupport.unpark(A);
},"B").start();
}
}
以上代码可见,线程A被B唤醒了
那么我们先执行拥有unpark()方法的B线程,结果又会是怎样呢?
public class Study {
public static void main(String[] args) {
Thread A = new Thread(()-> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A线程要被阻塞了");
LockSupport.park();
System.out.println("A线程出来了");
});
A.start();
new Thread(()->{
System.out.println("B要唤醒A了");
LockSupport.unpark(A);
},"B").start();
}
}
可见即使先执行了unpark(),A还是被唤醒了
以上可见,park-unpark的优点是:
①不相wait…要依赖synchronized、await…要依赖 ReentrantLock,,这个只需要调用LockSupport的静态方法即可,十分方便
②即使先执行了unpark()最终A还是被唤醒,可以避免因为线程的启动顺序导致线程阻塞