深入学习并发编程中的synchronized
第一章:并发编程中的三个问题
可见性
学习什么是可见性问题
可见性概念
可见性(Visibility):是==指一个线程对共享变量进行修改,另一个先立即得到修改后的最新值==。
可见性演示
案例演示:一个线程根据boolean类型的标记flag, while循环,另一个线程改变这个flag变量的值,另一个线程并不会停止循环。
package visablity;
public class VisablityTest {
/**
* 目标演示可见性问题
* 1.创建一个共享变量,
* 2、创建多条线程修改和读取共享变量
*/
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
// System.out.println("this is thread1");,这里不能加上输出语句
}
}, "Thread1").start();
//主线程休眠两秒,两秒之后启动新线程来讲我们的flag变为false;观察上面的线程是否还在继续执 //行,如果继续执行则就说明了上面的线程对我们的flag中的值变化不可见
Thread.sleep(2000);
new Thread(() -> {
flag = false;
System.out.println("线程修改了变量的值为false");
}, "Thread2").start();
//Thread1的循环一直在执行,没有中断,这就是并发变成中的可见性问题
}
}
| 并发编程时,会出现可见性问题,当一个线程对共享变量进行了修改,另外的线程并没有立即看到修改后的最新值。 |
原子性
目标
学习什么是原子性问题
原子性概念
原子性(Atomicity):在一次或多次操作中,==要么所有的操作都执行并且不会受其他因素干扰而中断,要么所有的操作都不执行==。
案例演示:
package atomic;
import java.util.ArrayList;
import java.util.List;
public class AtomicDemo {
//定义一个共享变量number
//对number进行1000次++操作
//使用5个线程来进行
private static int number = 0;
public static void main(String[] args) throws InterruptedException {
List<Thread> list = new ArrayList<>();
Runnable runnable = () -> {
for (int j = 0; j < 1000; j++) {
//让线程休眠,使的线程在这期间不能做任何的事情,当我从其他线程切换回当前的线程的时 //候,从原来的起点开始执行但是大人时代变了
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
number++;
}
};
for (int i = 0; i < 5; i++) {
Thread t = new Thread(runnable);
t.start();
list.add(t);
}
for (Thread thread : list) {
//等待线thread程死亡(执行结束)
//join方法其实就是阻塞当前调用它的线程,等待join执行完毕,当前线程继续执行
thread.join();
}
Thread.sleep(1000);
System.out.println("number is :" + number);
}
}
通过反汇编可以知道,number++是由多条语句组成,多条指令在一个线程的情况下是不会出问题的,但是在多线程情况下就可能会出现问题。比如一个线程在执行 时,另一个线程又执行 。会导致两次,实际上只加了1。
小结
并发编程时,会出现==原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共享变量==,干扰了前一个线程的操作。
有序性
程序代码在执行过程中的先后顺序,由于Java在编译期以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码时的顺序。