独角兽企业重金招聘Python工程师标准>>>
1.线程的通知与等待
Java中的Object类是所有类的父亲,鉴于继承机制,Java把所有类都需要的方法放到了Object类里面,其中就包含线程的通知与等待系列。
wait()方法
当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起,并释放该共享变量上的锁,直到下面几件事情之一才返回:
1)其他线程调用了该共享对象的notify()或者notifyAll()方法
2)其他线程调用了该线程的interrupt()方法,该线程抛出InterruptedException异常返回
另外需要注意的是调用共享变量的wait()方法前需要事先获取该对象的监视器锁,否则调用 wait()方法时调用线程会抛出IllegalMonitorStateException异常。
如何获取监视器锁?
a.执行synchronized同步代码块时,使用该共享变量作为参数
synchronized(共享变量) {// doSomething}
b.调用该共享变量的方法,并且该方法使用了synchronized修饰
synchronized void add(int a, int b) {// doSomething}
需要注意的是一个线程可以从阻塞挂起状态变为可运行状态(也就是被唤醒),即使该线程没有被其他线程调用notify()、notifyAll()方法进行通知,或者被中断,或者等待超时,这就是所谓的虚假唤醒。
防范虚假唤醒的一个方法就是在一个循环中调用wait()方法,退出循环的条件是满足了唤醒该线程的条件。
synchronized(obj) {while(条件不满足) {obj.wait();}}
wait(long timeout)方法
与wait()方法的不同之处在于,如果一个线程调用共享对象的该方法挂起后,没有指定的timeout ms时间内被其他线程调用该共享变量的notify()或者notifyAll()方法唤醒,那么该函数还是会因为超时返回。
notify()方法
一个线程调用共享对象的notify()方法后,会唤醒一个在该变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。
此外,被唤醒的线程不能马上从wait()方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是被唤醒的线程也不一定会获取到共享变量的监视器锁,这是因为该线程要和其他线程一起竞争该锁,只有竞争到了共享变量的监视器锁后才可以继续执行。
类似wait方法,只有当前线程获取到监视器锁后才可以调用notify()方法,否则会抛出IllegalMonitorStateException异常。
notifyAll()方法
与notify()方法的区别是,notify()会唤醒一个阻塞在该共享变量上的一个线程,notifyAll()方法则会唤醒所有在共享变量上的=由于被调用wait()方法而被挂起的线程。
一个生产者消费者的例子:
public class WaitNotifyTest3 {private static final Queue<String> queue = new LinkedBlockingQueue<>();private static final int MAX_SIZE = 1000;public static void main(String[] args) {Thread producer = new Thread(() -> {while (true) {synchronized (queue) {while(queue.size() == MAX_SIZE) {try {queue.wait();} catch(Exception ex) {ex.printStackTrace();}}queue.add("ele");queue.notify();}}});Thread consumer = new Thread(() -> {while (true) {synchronized (queue) {while(queue.size() == 0) {try {queue.wait();} catch(Exception ex) {ex.printStackTrace();}}System.out.println(queue.poll());queue.notify();}}});producer.start();consumer.start();}}
2.等待线程执行终止
在项目实践中经常会遇到一个场景就是需要等待某几件事情完成后才能继续往下执行,比如多个线程加载资源,需要等待多个线程全部加载完毕再汇总处理。Thread类中有一个join方法提供该功能。
看一个栗子:
public class JoinTest {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(() -> {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("child threadOne over");});Thread thread2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("child threadTwo over");});thread1.start();thread2.start();System.out.println("wait all child thread over");thread1.join();thread2.join();System.out.println("all child thread over");}}
另外线程A调用线程B的join方法后会被阻塞,当其他线程调用了线程A的interrupt()方法中断了线程A时,线程A会抛出InterruptedException异常而返回。如下:
public class JoinTest2 {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("thread1 begin run");for (; ; ) {}});final Thread threadMain = Thread.currentThread();Thread thread2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}threadMain.interrupt();});thread1.start();thread2.start();try {thread1.join();} catch (InterruptedException e) {e.printStackTrace();}}}
注意这里调用的是主线程的interrupted方法。