版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wrs120/article/details/90581073
文章目录
1. 线程间如何共享数据
使用全局变量或共享对象
2. 线程间变量为什么不可见
归根原因是由Java内存模型引起的,Java内存模型及操作规范:
- 共享变量必须存放在工作内存中
- 线程有自己的工作内存, 线程只可操作自己的工作内存
- 线程要想操作共享变量,需要从主内存中读取到工作内存,改变值后从工作内存同步到主内存中
根据上面描述,会出现这么一个问题:线程1已经从主内存读取变量a=1到工作内存中去了,但是在更改a=a+1后未更新到主内存之前,线程2又从主内存中读取了变量a=1,此时想想必然会引起数据不一致
主内存和工作内存之间交互遵从同步交互协议,规定了8中原子操作:
- lock锁定:将主内存中的变量锁定,为一个线程所独占
- unlock解锁:将lock假的锁定接触,此时其他线程有机会访问此变量
- read读取:作用于主内存变量,将主内存中的变量读取到工作内存当中去
- load载入:作用于工作内存变量,将read读取到的值保存到工作内存中的变量副本中
- use使用:作用于工作内存变量,将值传递给线程的代码执行引擎
- assign赋值:作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本
- store存储:作用于工作内存变量,将变量副本的值传递给主内存中
- write写入:作用于主内存变量,将store传送过来的值写入到主内存的共享变量中
注意: 每一步是原子操作,但是这8不不是一个原子操作
3.如何使线程间变量可见
3.1 final变量
变量本来就是不可改变的,不存在改变,所以不同线程间看到的数据永远一样
3.2synchronized
synchronized语义规范:
- 进入同步块之前,先清空工作内存中的共享变量,从主内存中重新加载
- 解锁前必须把修改的共享变量同步回主内存
synchronized是如何做到线程安全的:
- 锁机制保护共享资源,只有获得锁的线程才可操作共享资源
- synchronized语义规范保证了修改的共享资源后,会同步回主内存,做到了线程安全
综上:synchronized既实现了互斥性,也实现了可见性
3.3 volatile
使用场景
- 通过共享变量控制其它线程
- 加volatile修饰的变量所在的块不会进行指令重排优化(防止指令重排后,会出现抛空指针异常)
4. synchronzied与volatile比较
volatile 使用简单,性能也好,但是由于上面提到的同步交互协议中的8不操作不是一个整体的原子操作,会出现数据不一致问题,所以如果要保证数据的可见性和一致性,就要用synchronzied了