线程间通信

1.使用wait/notify实现线程间的通信

1.1 等待/通知机制的实现

方法wait()的作用是使当前执行代码的线程进行等待。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。
方法notify()也要在同步方法或同步代码块中调用。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则按优先级来获取,否则随机挑选一个呈wait状态的线程,对其发出通知notify,并使它进入就绪队列,也叫锁池。另外需要说明的是,在执行notify()方法之后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是推出synchronized代码块后,当前线程才会释放锁,然后由就绪队列里的线程争夺锁。
当一个获得了该对象的wait()线程运行完毕后,会释放该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。

1.2 方法wait(long)的使用

带一个参数的的wait(long)方法的功能是等待某一时间内是否有线程对锁唤醒,如果超过这个时间则自动唤醒。

1.3 等待wait的条件发生变化

如果采用if判断,当线程从wait中唤醒时,那么将直接执行处理其他业务逻辑的代码,但这时候可能出现另外一种可能,条件谓词已经不满足处理业务逻辑的条件了,从而出现错误的结果,于是有必要进行再一次判断,所有一般wait()方法是在while循环里,这样便于检测wait的条件是否变化。

2.生产者/消费者模式的实现

2.1 线程假死现象

“假死”现象其实就是线程全部进入WAITING等待状态,这时,程序不再执行任何业务功能,整个项目呈停止状态。
“假死”出现的原因是notify唤醒的时候,也许是异类,也许是同类,比如生产者唤醒生产者,或消费者唤醒消费者这样的情况。如果按这样运行的比率积少成多,最终就会导致所有的线程都不能继续运行下去,大家都在等待,都呈WAITING状态,就是”假死”。

3.方法join的使用

方法join()的作用是使所属的线程对象X正常执行run()方法中的任务,而使当前线程Z进行无限期的阻塞,等待线程X销毁后再继续执行线程Z后面的代码。

3.1 join与synchronized的区别

方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而sychroized该关键字使用的是”对象监视器”原理做为同步。

3.2 方法join(long)与sleep(long)的区别

join(long)的功能在内部是使用wait(long)方法实现的,所以join(long)方法具有释放锁的特点。而Thread.sleep(long)方法却不释放锁。

4.ThreadLocal的使用

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

4.1 ThreadLocal的内部结构图

www

从上面的结构图,我们已经窥见ThreadLocal的核心机制:

每个Thread线程内部都有一个Map。
Map里面存储线程本地对象(key)和线程的变量副本(value)
但是,Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。

所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。

4.2 ThreadLocal与InheritableThreadLocal的区别

ThreadLocal和InheritableThreadLocal本质上只是为了方便编码给的工具类,具体存数据是ThreadLocalMap 对象。
ThreadLocalMap 存的key对象是ThreadLocal,value就是真正需要存的业务对象。
Thread里通过两个变量持用ThreadLocalMap 对象,分别为:threadLocals和inheritableThreadLocals。
InheritableThreadLocal之所以能够完成线程间变量的传递,是在new Thread()的时候对inheritableThreadLocals对像里的值进行了复制。
子线程通过继承得到的InheritableThreadLocal里的值与父线程里的InheritableThreadLocal的值具有相同的引用,如果父子线程想实现不影响各自的对象,可以重写InheritableThreadLocal的childValue方法。

0%