目录
可见性
可见性概念
可见性演示
小结
原子性
原子性概念
原子性演示
小结
有序性
有序性概念
有序性演示
小结
可见性
可见性概念
可见性(Visibility):是指一个线程对共享变量进行修改,另一个先立即得到修改后的最新值
可见性演示
案例演示:一个线程根据boolean类型的标记flag,while循环,另一个线程改变这个flag变量的值,另一个线程并不会停止循环
/*** 案例演示:* 一个线程对共享变量的修改,另一个线程不能立即得到最新值*/
public class Test01Visibility {// 多个线程都会访问的数据,我们称为线程的共享数据private static boolean run = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (run) {}});t1.start();Thread.sleep(1000);Thread t2 = new Thread(() -> {run = false;System.out.println("时间到,线程2设置为false");});t2.start();}
}
小结
并发编程时,会出现可见性问题,当一个线程对共享变量进行了修改,另外的线程并没有立即看修改后的最新值
原子性
原子性概念
原子性(Atomicity):在一次或多次操作中,要么所有的操作都执行并且不会受其他因素干扰而中 断,要么所有的操作都不执行。
原子性演示
案例演示:5个线程各执行1000次 i++;
import java.util.ArrayList;/**案例演示:5个线程各执行1000次 i++;*/public class Test02Atomicity {private static int number = 0;public static void main(String[] args) throws InterruptedException {Runnable increment = () -> {for (int i = 0; i < 1000; i++) {number++;}};ArrayList<Thread> ts = new ArrayList<>();for (int i = 0; i < 5; i++) {Thread t = new Thread(increment);t.start();ts.add(t);}for (Thread t : ts) {t.join();}System.out.println("number = " + number);}}
使用javap反汇编class文件,得到下面的字节码指令:

其中,对于 number++ 而言(number 为静态变量),实际会产生如下的 JVM 字节码指令:
9: getstatic #12 // Field number:I
12: iconst_1
13: iadd
14: putstatic #12 // Field number:I
由此可见number++是由多条语句组成,以上多条指令在一个线程的情况下是不会出问题的,但是在多 线程情况下就可能会出现问题。比如一个线程在执行13: iadd时,另一个线程又执行9: getstatic。会导致两次number++,实际上只加了1。
小结
并发编程时,会出现原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共 享变量,干扰了前一个线程的操作。
有序性
有序性概念
有序性(Ordering):是指程序中代码的执行顺序,Java在编译时和运行时会对代码进行优化,会导致 程序最终的执行顺序不一定就是我们编写代码时的顺序。
public static void main(String[] args) {int a = 10;int b = 20;
}
有序性演示
jcstress是java并发压测工具。https://wiki.openjdk.java.net/display/CodeTools/jcstress
修改pom文件,添加依赖:
<dependency><groupId>org.openjdk.jcstress</groupId><artifactId>jcstress-core</artifactId><version>${jcstress.version}</version>
</dependency>
代码
Test03Orderliness.java
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")@Statepublic class Test03Orderliness {int num = 0;boolean ready = false;// 线程一执行的代码@Actorpublic void actor1(I_Result r) {if(ready) {r.r1 = num + num;} else {r.r1 = 1;}}// 线程2执行的代码@Actorpublic void actor2(I_Result r) {num = 2;ready = true;}}
I_Result 是一个对象,有一个属性 r1 用来保存结果,在多线程情况下可能出现几种结果?
情况1:线程1先执行actor1,这时ready = false,所以进入else分支结果为1。
情况2:线程2执行到actor2,执行了num = 2;和ready = true,线程1执行,这回进入 if 分支,结果为 4。
情况3:线程2先执行actor2,只执行num = 2;但没来得及执行 ready = true,线程1执行,还是进入 else分支,结果为1。
运行测试:
mvn clean install
java -jar target/jcstress.jar
小结
程序代码在执行过程中的先后顺序,由于Java在编译期以及运行期的优化,导致了代码的执行顺序未必 就是开发者编写代码时的顺序。