线程间通信
参考:
https://zhuanlan.zhihu.com/p/452313580
https://zhuanlan.zhihu.com/p/34362413
https://zhuanlan.zhihu.com/p/151289085
https://www.cnblogs.com/bearbrick0/p/15904552.html
介绍
线程启动后,它会在自己独有的栈空间里面运行,但是实际上,两个线程之间是会相互通信的,因为只有这样才能使线程间更加灵活,使资源使用的更加充分。
多线程之间相互协调完成工作的过程叫线程间通信。
可见性
可见性是指:当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。
背景:
两个线程对同一个共享变量进行操作,其中一个线程对其修改,另外一个线程是看不到这个变化的。
由jvm内存模型决定,内存模型分为共享区域和线程私有区域,线程启动后会把共享区域(主内存区)的变量作为副本存到自己内部(工作内存区),而工作内存的数据不会实时刷到主内存,所以当线程修改变量时,只是对自己生效,其他线程并不会感知到,看下图:
线程对变量的所有操作都必须在工作内存中进行而不能直接操作主内存的变量
不同线程之间也无法直接访问对方工作内存中的变量
线程间变量值的传递均需通过主内存来完成
主内存与工作内存的交互遵守及一定的协议
Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的无论是普通变量还是volatile变量都是如此,普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。因此,可以说volatile保证了多线程操作时变量的可见性,而普通变量则不能保证这一点。
volatil
保证对一个变量的更新能够立马被其他线程看到
当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是会把值刷新回主内存。
当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。
Java内存模型对volatile专门定义了一些特殊的访问规则,当一个变量定义为volatile之后,它将具备两种特性。- 保证此变量对所有线程的可见性,即当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。而普通变量不能做到这一点,普通变量的值在线程间传递均需要通过主内存来完成,例如,线程A修改一个普通变量的值,然后向主内存进行回写,另外一条线程B在线程A回写完成了之后再从主内存进行读取操作,新变量值才会对线程B可见。
- 禁止指令重排序优化。普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。因为在一个线程的方法执行过程中无法感知到这点,这也就是Java内存模型中描述的所谓的“线程内表现为串行的语义”(Within-Thread As-If-Serial Semantics)。
只能保证可见性,不能保证原子性(需要通过加锁保证原子性)
能够禁止指令重排,在一定程度上保证代码有序性
线程间通信方式
主要分为共享内存、消息传递(也叫等待-通知)和管道流
共享内存:线程间读写共享内存中的公共数据实现间接通信,比如 volatile 保证内存的可见性
消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。比如 wait/notify/notifyAll等待通知方式和join方式。
管道流:管道流是是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。比如 管道输入/输出。管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信。像消息传递机制,也就是说:通过管道,将一个线程中的消息发送给另一个。
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.