一,什么是线程间的通信?
线程间的通信说到底就是线程间的相互交互或者说是相互配合来完成既定的工作,如果多个线程间是独立的存在,那么多线程是毫无意义
二,线程通信应用
1.volatile关键字
java支持多个线程同时访问同一个对象或者对象的成员变量
每个线程可以拥有这个变量的一份拷贝,这是因为虽然对象以及成员变量分配的内存是在共享内存中的,但是每个执行的线程还是可以拥有一份拷贝,这样做的目的是加快程序的执行,只是现代多核处理器的一个显著特性
所以在程序执行过程中,一个线程看到的变量并不一定是最新的
如何解决这个问题呢?
关键字volatile可以用来修饰字段,告知程序任何对该变量的访问必须要从共享内存中获取,而对他的改变必须同步刷新回共享内存中,保证所有线程对变量的访问的可见性
例子:
volatile boolean isOn = true;
另一个线程对isOn执行false操作,涉及到多个线程对同一个变量的访问和修改时需要将变量用volatile修饰,保证让所有线程感知到该变量的变化,所有修改都要以共享内存为准
volatile缺点
过多使用volatile关键修饰是不必要的,会降低程序执行效率
2.synchronized关键字
关键字synchronized可以修饰方法或者代码块,主要保证在多线程环境下,在一个时刻只有一个线程处于同步代码块或者方法中,保证了线程对变量访问的可见性和排他性
//同步代码块
synchronized (TestThread.class){
}
//同步方法
public synchronized void test(){
}
synchronized执行流程
每个对象都有一个对象的监视器,当这个对象被同步代码块或者同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步方法或者同步代码块,没有获取到监视器的则进入同步队列,线程进入阻塞状态
3.等待/通知机制
等待/通知的相关方法是每个对象都有的,因为这些方法被定义在Object上
方法 | 作用 |
---|---|
notify() | 唤醒在此对象监视器上等待的单个线程。如果有多个线程都在此对象上等待,则会随机选择唤醒其中一个线程 |
notifyAll() | 唤醒所有等待在对象上的线程 |
wait() | 线程进入等待状态,只有另外线程通知唤醒才会返回运行状态 |
wait(long) | 等待指定时间,没有另外线程唤醒就超时才返回运行状态 |
@Slf4j
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
static class WaitThread implements Runnable{
@Override
public void run() {
synchronized (lock){
while(flag){
log.info("flag -------> true");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("flag -------------> false in NotifyThread after");
}
}
}
static class NotifyThread implements Runnable{
@Override
public void run() {
synchronized (lock){
log.info("flag -------------> true in Notify before");
lock.notify();
flag = false;
log.info("flag ---------> false");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lock){
log.info("-------------------------");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new WaitThread());
thread1.start();
Thread.sleep(2000);
Thread thread = new Thread(new NotifyThread());
thread.start();
}
}
补充:
-
使用wait(),notify(),notifyAll()需要先对调用对象加锁,比如lock对象
-
使用notify(),notifyAll()后,等待线程不会马上从wait()中返回,需要调用的方法线程释放锁后才有机会从wait()返回
-
notify()将等待队列中的一个等待线程(wait())从等待队列中移到同步队列中
-
notifyAll()将等待队列中所有线程移到同步队列,状态变化:等待—>阻塞
4.等待超时机制
等待超时模式就是在等待/通知机制上增加了超时控制,即使方法执行时间过长,也不会永久阻塞调用者,而是会按照调用者的要求按时返回。
public synchronized Object get(long mills) throws InterruptedException {
long future = System.currentTimeMillis() + mills;
long remaining = mills;
//当超时大于0并且result返回值不满足条件
while ((result == null) && remaining > 0) {
wait(remaining);
remaining = future - System.currentTimeMillis();
}
return result;
}