1. 可见性
指一个线程对共享变量进行修改,另一个线程立即得到修改后的值
测试代码
public class Test01Visibility {
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
//千万别打印,打印就没有效果
}
}).start();
Thread.sleep(2000);
new Thread(() -> {
flag = false;
System.out.println("线程修改了值为false");
}).start();
}
}
结果是值修改成了false了,但是程序还没结束进入死循环
线程2对共享变量进行了修改,但线程1却不知道。
2. 原子性
指一次或多次操作,要么所有操作执行成功并且不受其他因素干扰而停止,要么就是所有操作都不成功。
测试代码
public class Test02Atomicity {
private static int number = 0;
public static void main(String[] args) throws InterruptedException {
List<Thread> list = new ArrayList<Thread>();
for (int i = 0; i < 5; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
number++;
}
});
t.start();
list.add(t);
}
//免得主线程跑完了
for (Thread thread : list) {
thread.join();
}
System.out.println(number);
}
}
最后运行有可能少于5000
这是因为number++;并不是一个原子性操作,通过javap -p -v反编译该class文件,可以看到其由4条指令构成
分别对应的大概意思是,(1)获取值,(2)准备常量1,(3)值+常量1,(4)把结果返回。
因此多个线程操作的时候,比如有可能线程1获取值的是100,正要准备100+1前,执行线程2了,此时线程2也获取到值100,也是100+1,最后线程1,和线程2都把101值返回到变量中。即做了两次加法但是结果只加了1.
3. 有序性
指程序中代码的执行顺序,java在编译和运行时会对代码进行优化,会导致最后程序的执行顺序不一定是我们编写代码的顺序
这个测试类有点难写,借用了一个jsrtress包
<dependency>
<groupId>org.openjdk.jcstress</groupId>
<artifactId>jcstress-core</artifactId>
<version>0.7</version>
</dependency>
package demo1;
import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.I_Result;
@JCStressTest
@Outcome(id={"1","4"},expect = Expect.ACCEPTABLE,desc="ok")
@Outcome(id={"0"},expect = Expect.ACCEPTABLE_INTERESTING,desc="danger")
@State
public class Test03Ordering {
int num = 0;
boolean ready = false;
@Actor
public void actor1(I_Result r){
if (ready) {
r.r1 = num + num;
} else {
r.r1 = 1;
}
}
@Actor
public void actor2(I_Result r){
num = 2;
ready = true;
}
}
大家可以自行百度一下怎么玩jcstress,这里就不多说了,只说结果,因为java对代码进行了优化,所以有可能 ready = true;会在 num = 2;前面折行,得到了一个顺序执行时不可能出现的结果(结果为0).
博主的坚持 离不开大家关注、评论和点赞,感谢大家支持。护发路上互相扶持!!!