现在我们来学习第一章的内容,并发编程中存在的三个问题。
我们在使用多线程进行并发编程的时候,如果同时有多个线程来操作共享数据,那么很有可能这个共享数据它的值会出现错乱,那么我们称之为线程安全问题。那么导致这个线程安全问题的根本原因有这三个,第一个是可见性问题,第二个是原子性问题,第三个是有序性问题。那么我们这一章节就重点来学习这三个问题。
可见性问题
目标:那么咱们先看第一个目标,学习什么是可见性问题。咱们讲解的步骤分成两个。第一个介绍可见性的概念,第二个通过一个案例来演示可见性问题。
那么我们在讲解可见性概念之前,要注意他的几个前提条件。第一个如果只有一个线程来操作,那么就肯定不会存在这个线程间的可见性问题。第二个还需要有共享数据,如果没有共享数据,那么也不存在可见性的问题。
可见性是指一个线程对共享变量进行修改,另一个线程要立即得到修改后的最新值。
接下来我们就通过代码来演示可见性问题。
案例:
目标:演示可见性问题。
步骤:
1、创建一个共享变量。
2、要创建多条线程,其中一条线程来不断读取共享变量,另外再创建一条线程,然后修改共享,我们看前一条线程能不能立马得到修改后的最新值。
public class VisibilityTestDemo01 {
// 1、创建一个共享变量
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
// 2、创建一条线程不断读取共享变量
new Thread(() -> {
while (flag) {
}
}).start();
//让线程休眠 2 秒
Thread.sleep(2000);
//3、创建一条线程修改共享变量
new Thread(() -> {
flag =false;
System.out.println("修改共享变量的线程修改了变量的值为 false。");
}).start();
}
}
我们先来分析一下这段代码,我们程序 main 方法执行,然后开启了一个读取共享变量线程,一个修改共享变量线程,我们多线程执行具有随机性,有可能先跑读取共享变量线程的。有可能先跑修改共享变量线程,如果先跑修改共享变量线程的,咱们就看不到循环了。为了让这个效果更加明显一点,这里加一个 sleep 方法,让线程休眠 2 秒,这样的话就更加明显的来分析问题。
按照我们现在所学的知识点。那么首先执行 main 方法的时候,会创建出一个读取共享变量线程,那么这个线程会来读取共享变量值。所读取到的是 flag 的值是 true,所以 while 循环会一直进行,当主线程数了两秒之后,又会启动一个修改共享变量线程,将 flag 的值变成了 false。那么问题是读取共享变量线程中的 flag 的值有没有变成 false 呢?如果有,那么他就会停下来。如果没有,他就还会继续。
程序运行的结果:
在两秒看这边已经输出了线程将 flag 值修改 false。但是咱们的程序并没有结束,也就意味着读取共享变量线程还在 while 循环中执行着,认为 while 中的的变量还是 true,所以还在循环并没有停止。那么这个时候同学们就看到了,是不是咱们并发编程就出现了可见性问题,修改共享变量线程对变量的修改,我们读取共享变量线程并没有立即得到最新的结果。
小结:当我们并发编程的时候,如果有多线程来操作共享变量,一个线程来读,一个线程来写,那么可能会出现线程安全问题,就是一个线程进行变量的修改,另外一个线程并没有立马得到修改后的最新值。