文章目录
- 1. 线程间的通信
- 1.1 wait和notify
- 1.2 notify随机唤醒
- 1.3 notifyAll()
- 1.4 join()
- 2. 线程间的状态
- 3. 验证线程的状态
- 3.1 验证NEW、RUNNABLE、TERMINATED
- 3.2 验证WAITING
- 3.3 验证TIMED-WAITING
- 3.4 验证BLOCKED
- 4. 面试题:wait和sleep对比
1. 线程间的通信
1.1 wait和notify
由于线程之间是抢占式运行,所以无法预知线程的执行顺序,但是实际开发中我们需要协调线程间的执行先后顺序。所以我们引入了等待通知机制。
wait()方法:
会使一个线程进入堵塞,进入等待状态,等待被唤醒,不然永远不会执行,但必须搭配synchronized使用,不然就会抛出异常,执行后释放锁。
notify()方法:
会随机唤醒一个等待的线程(不是先来后到),也需要搭配synchronized使用,但是只有执行完同步代码块才会释放锁。
notifyAll()方法
一次将所有等待的线程唤醒,其他和wait方法相同。
创建线程类ThreadA.java:
public class MyThreadA extends Thread{
private Object lock;
public MyThreadA(Object lock){
this.lock = lock;
}
@Override
public void run() {
try{
synchronized (lock){
System.out.println(Thread.currentThread().getName() + ": begin");
lock.wait();//死等,释放锁
System.out.println(Thread.currentThread().getName() + ": end");
}
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
创建线程类ThreadB.java:
public class MyThreadB extends Thread{
private Object lock;
public MyThreadB(Object lock){
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("t2: begin");
lock.notify();//唤醒一个线程,随机的,不释放锁
System.out.println("t2: end");
}
}
}
创建运行类Running.java:
public class Running {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
MyThreadA t1 = new MyThreadA(lock);
MyThreadB t2 = new MyThreadB(lock);
t1.start();
Thread.sleep(1000);
t2.start();
}
}
如果等待机制成立,那么运行过程应该是先启动线程t1,然后打印"Thread-0: begin",然后执行wait(),进入等待并释放锁,然后在启动t2后会先打印"t2: begin",然后唤醒t1,但是因为锁还没释放,会继续执行t2后面代码,打印"t2: end",最后释放锁,执行t1,打印"Thread-0: end",如果运行结果和我们分析相同,那么等待机制成立。
运行结果:
1.2 notify随机唤醒
修改运行类:Running.java:
public class Running {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
MyThreadA t1 = new MyThreadA(lock);
MyThreadA t2 = new MyThreadA(lock);
MyThreadA t3 = new MyThreadA(lock);
MyThreadB t4 = new MyThreadB(lock);
t1.start();
t2.start();
t3.start();
Thread.sleep(1000);
t4.start();
}
}
多次运行结果如果不相同,则notify唤醒不遵循先来后到。
运行结果:
1.3 notifyAll()
修改线程类ThreadB.java:
public class MyThreadB extends Thread{
private Object lock;
public MyThreadB(Object lock){
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
System.out.println("t2: begin");
lock.notifyAll();//唤醒全部线程
// lock.notify();//唤醒一个线程,随机的,不释放锁
System.out.println("t2: end");
}
}
}
运行结果:
很明显唤醒了全部线程。
1.4 join()
有时我们需要等待一个线程运行结束,在执行其他线程,这个时候我们就可以使用join()方法。
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("t1执行");
});
Thread t2 = new Thread(() -> {
System.out.println("t2执行");
});
t2.start();
t2.join();
t1.start();
}
}
分析代码,t2调用join(),那么只有t2线程执行结束才可以执行下面代码,也就是t1。那么如果是:t2执行,t1执行,那么join成立。
运行结果:
2. 线程间的状态
线程对象在不同时期有不同状态,这些状态都在枚举类State中,则:
public class MyThreadState {
//线程状态,都在State枚举类中
public static void main(String[] args) {
for (Thread.State state :
Thread.State.values()) {
System.out.println(state);
}
}
}
运行结果:
NEW:已经安排了工作,但未启动的线程对象。
RUNNABLE:可以运行中的线程对象,可分为正在运行,即将运行。
BLOCKED:受阻塞,等待某个监视锁的线程对象。
WAITING:无期限的等待另一个线程执行特定操作的线程。
TIMED_WIATING:指定等待时间等待另一个线程的操作的线程对象。
TERMINATED:工作结束。
3. 验证线程的状态
3.1 验证NEW、RUNNABLE、TERMINATED
实例化一个线程未启动,这时线程就是NEW状态,当启动后,运行中就是RUNNABLE状态,等待线程运行结束,就是TERMINATED状态。
//验证: NEW RUNNABLE TERMINATED
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("运行中: " + Thread.currentThread().getState());
});
System.out.println("main中: " + t1.getState());
Thread.sleep(1000);
t1.start();
Thread.sleep(1000);
System.out.println("运行结束: " +t1.getState());
}
运行结果:
3.2 验证WAITING
给一个线程上锁,不解锁,让它一直等待,则线程处于WAITING状态。
//验证: WAITING
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(() -> {
System.out.println("运行中:");
try{
synchronized (lock){
lock.wait();
}
}catch (InterruptedException e){
e.printStackTrace();
}
});
t1.start();
Thread.sleep(1000);
System.out.println("无休止等待: " + t1.getState());
}
运行结果:
3.3 验证TIMED-WAITING
给一个线程休眠,休眠中查看它的状态,则应是TIMED-WAITING状态。
//验证: TIMED_WAITING
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("运行中: " + Thread.currentThread().getState());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
Thread.sleep(1000);
System.out.println("指定时间等待: " + t1.getState());
}
运行结果:
3.4 验证BLOCKED
当一个线程等待另一个线程而堵塞,则应是BLOCKED状态。
//验证: BLOCKED
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread t1 = new Thread(() -> {
System.out.println("t1: " + Thread.currentThread().getState());
try{
synchronized (lock){
Thread.sleep(10000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
synchronized (lock){
System.out.println("t2: " + Thread.currentThread().getState());
}
});
t1.start();
Thread.sleep(1000);
t2.start();
Thread.sleep(1000);
System.out.println("t2: " + t2.getState());
}
运行结果:
4. 面试题:wait和sleep对比
- 一个用于线程通信,一个用于线程堵塞,唯一的相同点就是使线程放弃了一部分执行时间。
- wait需要搭配synchronized使用,sleep不用。
- wait使Object的方法,sleep使Thread的静态方法。