多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同,于是这些线程之间就存在通信问题,称为线程间通信。
比如:生产者消费者问题。
当多个线程间存在通信问题时,我们希望它们能有规律地执行,因此就需要一些协调手段,其中,等待唤醒机制就是协调线程间通信的一种有效手段。
public static void main(String[] args) {
Object monitor = new Object();
new Thread(() -> {
synchronized (monitor) {
System.out.println("线程1执行");
try {
monitor.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("线程2执行");
synchronized (monitor) {
monitor.notify();
}
}).start();
}
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(() -> {
System.out.println("线程1执行");
lock.lock();
try{
condition.await();
System.out.println("线程1被唤醒");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("线程2执行");
lock.lock();
try{
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个permit。但与Semaphore不同的是,许可的累加上限是1
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("线程1执行");
LockSupport.park();
System.out.println("线程1被唤醒");
});
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("线程2执行");
LockSupport.unpark(t1);
}).start();
}
为什么推荐使用LockSupport
来做线程的阻塞与唤醒(线程间协同工作),因为它具备如下优点
notify
随机唤醒一个线程,notifyAll
唤醒所有等待的线程)unpark
与park
没有严格的执行顺序,不会因执行顺序引起死锁问题,比如「Thread.suspend
和Thread.resume
」没按照严格顺序执行,就会产生死锁另外LockSupport
还提供了park
的重载函数,提升灵活性
void parkNanos(long nanos)
:增加了超时机制void parkUntil(long deadline)
:加入超时机制(指定到某个时间点,1970
年到指定时间点的毫秒数)void park(Object blocker)
:设置blocker
对象,当线程没有许可证被阻塞时,该对象会被记录到该线程的内部,方便后续使用诊断工具进行问题排查void parkNanos(Object blocker, long nanos)
:设置blocker
对象,加入超时机制void parkUntil(Object blocker, long deadline)
:设置blocker
对象,加入超时机制(指定到某个时间点,1970
年到指定时间点的毫秒数)建议使用时,传入blocker
对象,至于超时根据业务场景选择