写在前面:
线程之间如果一直处于独立运行,并不会为我们的项目带来多大的价值,如果线程之间一直保持通信,则会带来巨大的的价值,而这篇博客将总结 volatile 和 synchronized 这两个维护线程之间通信的关键字。
volatile关键字
volatile用于修饰字段(成员变量),volatile的使用就是告诉线程对变量的访问,必须从共享内存中获取,而不会利用缓存,而且对变量的改变,必须同步刷新回共享内存。
synchronized关键字
用于修饰方法或者以同步块的形式来使用。确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中。保证了线程对变量访问的排他性。
等待/通知机制
等待、通知的相关方法
方法名称 | 描述 |
notify | 通知一个在对象上等待的线程,使其从wait方法中返回,前提是获取到了对象的锁 |
notifyAll | 通知所有等待在该对象上的线程 |
wait | 调用该方法的线程会进入WAITING状态,只有等待其他线程的通知或中断才会返回 注意:当调用了wait方法后,就会释放对象的锁 |
wait(long) | 超时等待一段时间,如果没有通知就超时返回 |
wait(long,int) | 对于超时等待更加细粒度的控制,到纳秒级别 |
package test6;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Test4 {
static Object lock = new Object();
static boolean flag = true;
public static void main(String[] args) throws Exception {
Thread waitThread = new Thread(new Wait(), "waitThread");
waitThread.start();
TimeUnit.SECONDS.sleep(1); // TimeUnit是枚举类
Thread notifyThread = new Thread(new Notify(), "notifyThread");
notifyThread.start();
}
static class Wait implements Runnable {
@Override
public void run() {
synchronized (lock) {
while (flag) {
try {
System.out.println(Thread.currentThread()+"flag is true wait"
+new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread()+"flag is false running"
+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable {
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread()+"hold lock.notify"
+new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lock) {
System.out.println(Thread.currentThread()+"hold lock again sleep"
+new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
程序执行流程
(1)定义了两个线程,线程Wait先启动,打印,然后调用了lock的wait方法,释放锁
(2)此时线程Wait睡眠1秒
(3)线程Notify启动,获得锁,调用notifyAll方法,将所有等待的线程都移到同步队列,并将flag设置为flase。
(4)Notify线程睡眠5秒
(5)Notify线程再次获得锁,再睡眠5秒
(6)线程Notify获得锁,因为flag=false,所以不执行while循环内的内容。
控制台输出信息如下:
Thread[waitThread,5,main]flag is true wait22:14:19
Thread[notifyThread,5,main]hold lock.notify22:14:20
Thread[notifyThread,5,main]hold lock again sleep22:14:25
Thread[waitThread,5,main]flag is false running22:14:30
总结:
在调用wait方法后,线程状态会由RUNNING变成WAITING,并会将当前线程放置到对象的等待队列。
在调用notify方法,线程会从等待队列移动到同步队列。线程状态会从WAITING变成BLOCKED状态。