java 内存模型
概念
定义了线程在访问内存时候会发生什么
重排序
概念
编译器和java虚拟机为了让程序运行的更有效率改变了原有代码的执行顺序
这个执行顺序的改变就是重排序
引发的问题
一般情况下重排序没什么影响
仿佛对程序员并没有感知到重排序一样
而在多线程程序中这种自动的重排序可能造成运行错误
举例
思路
创建两个线程分别调用数据类的两个方法
这两个方法其中一个方法中代码满足重排序的可能
这种重排序会影响另一个方法的逻辑正确性
核心代码
public class test{
private int i = 0;
private int j = 0;
public void setData(){
i = 100;
j = 50;
}
public void show(){
if(i < j){
System.out.println("i < j");
}
}
}
测试类
public class Main {
public static void main(String[] args){
final test t = new test();
new Thread(){
public void run(){
t.setData();
}
}.start();
new Thread(){
public void run(){
t.show();
}
}.start();
}
}
输出
(几次都是空白)
说明
……
说明按照图解java多线程中的例子不管用嘛…
P448
原理
由于i = 100 和 j = 50之间的顺序不存在任何逻辑依赖性
所以可能被重排序 进而 变成 j = 50 在 i = 100之前
又由于 两个线程并发,那么在j = 50 之后可能 另一个线程上了cup
然后执行了i < j 的判断,此时 i = 0 , j = 50 显然成立,然后打印
然而这样是和原来顺序的直观逻辑不一致的
这就是重排序带来影响
解决
这种设计的错误称为为正确同步
一般都能通过synchronized和volatile解决
本例只要在两个方法上加上synchronized即可
可见性
概念
可见就是可读
A线程对i字段写了一个值
B线程可以读到这个i字段的值
那么线程A对i的写值对B就是可见的
按书里的定义这个可见就是写值可见
其他线程读到的是写了之后的值
而不是写的过程中的值
问题
对多线程可能造成以为可读但事实上短暂不可读情况出现
举例
思路
线程类中run方法根据一个默认为真的条件设置一个死循环
再设置一个set方法让这个条件为假
目的是通过调用set方法让自动调用的run脱离死循环进行下面的操作
然而set方法的线程和run方法的线程不是同一个
条件的赋值有可能不可见,那么run方法可能永远都在死循环了
核心代码
public class test extends Thread{
private boolean flag = false;
public void run(){
while(!flag){}
System.out.println("脱离死循环了!");
}
public void set(){
flag = true;
}
}
测试类
public class Main {
public static void main(String[] args){
test t = new test();
t.start();
t.set();
}
}
输出
脱离死循环了!
说明
java多线程设计模式,这本书有毒…
解决
书里说的是把flag设置成 volatile即可…
volatile
被volatile修饰的字段具备可见性
volatile保持可见性有性能代价
volatile不具备互斥性,也就是线程遇到volatile不会去请求锁
java api中没有long和double的原子操作类
但是volatile可以作用在long和double上
共享内存与缓存的读写与可见性
共享内存
实例对象数据都在共享内存中
由多个线程共享
缓存
局部变量都在栈中
由各个线程独占
局部变量的读写首先会访问各个线程独立的缓存
各个线程的缓存之间是不可见的
想要变得可见就的利用共享内存
具备可见性的字段写值到缓存后会强制写到共享内存中
然后读的线程会强制让自己的缓存失效直接把共享内存的值写到局部缓存中
然后读的是可见的正确的值
synchronized的同步性和互斥性
互斥性
也就是锁的互斥
只允许一个线程能拿到锁
多个想拿到锁的线程是互斥的
对锁是独占的
同步性
同步就是多个线程并发的时候数据的完整性是一致的同步的
一个线程更新了值另一个线程读到的值也是更新的
这个更新对所有并发的线程是同步的
原理
也就是互斥实现的
互斥保证了一个线程操作的完整性
操作完整当然也就能读到正确的值了
由于同步性与互斥性
又可以说synchronized能保证线程间不受重排序和可见性的影响