并发编程中数据的可见性

发布于:2024-12-18 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、什么是并发编程的可见性?

  在并发编程中,“可见性”是指一个线程对共享变量的修改是否能被其他线程及时看到的特性。

二、不可见情况的测试

    现在设置成员属性flag=true,如果flag=true则t1线程一直死循环执行任务,main线程设置flag=false,那么t1线程按道理应该结束任务,代码如下:

public class NoVisibleTest {

    private static volatile boolean flag = true;
    
    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            while (flag) {
                //  死循环执行任务
            }
            System.out.println("t1线程任务执行结束");
        }, "t1").start();

        //  主线程休眠1s,等待t1进入循环
        Thread.sleep(1000);

        //  修改flag为false
        flag = false;
        System.out.println("main线程执行结束");
    }
}

  可执行结果是 main线程执行结束,t1线程仍在一直死循环执行。要想搞清楚其中的原理就要理解底层。

三、计算机硬件简单分布结构

    每一个CPU都会有自己的三级缓存(L1、L2、L3),并且自己CPU的三级缓存不能被其他CPU访问,内存的访问速度相对于CPU实际上是很慢的,让CPU直接访问内存效率很低,所以加入三级缓存提高CPU的执行效率

    但是CPU执行修改操作时只是将三级缓存给修改了,并没有修改内存,这就导致虽然main中的flag=false,但是t1线程并不知道

四、解决方案

  解决方案一:

    在flag属性前加上volatile关键字修饰,

private static volatile boolean flag = true;

    作用是令三级缓存中的flag属性失效,这样每次访问flag都要从内存重新读取,每次修改都会立刻同步到内存

  解决方案二:

    在while循环内部加上synchronized,如:

 while (flag) {
     synchronized(NoVisibleTest.class) {
         
     }
 }

​     原理是synchronized每次抢到锁、释放锁,都会将三级缓存与主存进行同步,这就保证了数据的可见性

其他解决方案:

​     在while循环内部使用System.out.println()语句,因为内部使用了synchronized;

​     使用AtomicInteger类,内部使用了volatile关键字