文章目录
- 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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 TERMINATEDpublic 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状态。
//验证: WAITINGpublic 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_WAITINGpublic 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状态。
//验证: BLOCKEDpublic 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的静态方法。