JUC-001-volatile与内存可见性

JUC简介

  • 在Java 5.0 提供了java.util.concurrent(简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现等。

内存可见性(Memory Visibility)

  • 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
  • 可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。
  • 我们可以通过同步来保证对象被安全地发布。除此之外我们也可以使用一种更加轻量级的volatile 变量。

《JUC-001-volatile与内存可见性》

测试代码

package top.itcourse._volatile;

import org.junit.Test;
import org.omg.CORBA.FloatSeqHelper;

/* * 内存可见性(Memory Visibility): * - 内存可见性(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象 * 状态后,其他线程能够看到发生的状态变化。 * - 可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根 * 本不可能的事情。 * - 我们可以通过同步来保证对象被安全地发布。除此之外我们也可以使用一种更加轻量级的volatile 变量。 * * 说明: * 线程对象中的属性是放在主存中的,线程修改/读取的属性是在自己线程的缓存中,当修改完毕之后再把值 * 放回主存(不同的线程中去读取/修改的属性值,都是主存中的值) */


/* * 下面我们实现: * 线程一、二都读取到flag的初始值,然后在线程一的run方法中改变flag的值,但是线程二的flag值没有同步更新 */
public class TestMemVisi {
    @Test
    public void testMemVisi() throws InterruptedException {
        // 2.新建实现了Runnable的对象
        MemVisi mv1 = new MemVisi();
        // 3.传递到Thread对象中start
        new Thread(mv1).start();

        while(true) {
            // 线程二:一直是false(因为线程一sleep的时候,它进来了,又因为是while循环,使得线程二根本没有机会再次从主存中去读取新值(可以说明
            // 没有线程间没有同步的通知、更新))
            if( mv1.isflag() ) {
                System.out.println("我在主线程打印,flag是true");
            }

            // Thread.sleep(50); // 加上这句话,就可以有机会去主存中读取新值
        }
    }
}

// 1.实现Runnable
class MemVisi implements Runnable {

    private boolean flag = false;

    public boolean isflag() {
        return flag;
    }

    public void setflag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 让线程二先进去判断(while判断) 
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }

        // 线程一: 
        flag = true;
        System.out.println("flag: " + flag);
    }
}

运行结果:
flag: true

volatile 关键字

  • Java提供了一种稍弱的同步机制,即volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将volatile 看做一个轻量级的锁,但是又与锁有些不同:

    • 对于多线程,不是一种互斥关系
    • 不能保证变量状态的“原子性操作”
  • 计算机在运行程序时,每条指令都是在CPU中执行的,在执行过程中势必会涉及到数据的读写。我们知道程序运行的数据是存储在主存中,这时就会有一个问题,读写主存中的数据没有CPU中执行指令的速度快,如果任何的交互都需要与主存打交道则会大大影响效率,所以就有了CPU高速缓存。CPU高速缓存为某个CPU独有,只与在该CPU运行的线程有关。

  • 有了CPU高速缓存虽然解决了效率问题,但是它会带来一个新的问题:数据一致性。在程序运行中,会将运行所需要的数据复制一份到CPU高速缓存中,在进行运算时CPU不再也主存打交道,而是直接从高速缓存中读写数据,只有当运行结束后才会将数据刷新到主存中。

  • 解决方案:
    • 加锁
      • 独占,只能有一个运行,其它都得阻塞
    • 缓存一致性协议(volatitle)
      • 缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个CPU在写数据时,如果发现操作的变量是共享变量,则会通知其他CPU告知该变量的缓存行是无效的,因此其他CPU在读取该变量时,发现其无效会重新从主存中加载数据。
        《JUC-001-volatile与内存可见性》

测试代码

package top.itcourse._volatile;

import org.junit.Test;

/* * volatile关键字: * Java提供了一种稍弱的同步机制,即volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将volatile 看做一个轻量级的锁,但是又与锁有些不同: * - 对于多线程,不是一种互斥关系(同步通知,所以效率会高一些) * - 不能保证变量状态的“原子性操作”(下节讲) * * 作用:多个线程操作共享数据时,保证内存中的数据是可见的 */

/* * 解决同步通知、更新的问题: * 1.使用锁(效率低) * 2. */
public class TestVolatile {
    @Test
    public void testMemVisi() throws InterruptedException {
        // 2.新建实现了Runnable的对象
        MemVisia mv1 = new MemVisia();
        // 3.传递到Thread对象中start
        new Thread(mv1).start();

        while(true) {
            // 使用synchronized,可以解决,会去刷新缓存 
// synchronized(mv1) {
// if( mv1.isflag() ) {
// System.out.println("我在主线程打印,flag是true");
// break;
// }
// }

            if( mv1.isflag() ) {
                System.out.println("我在主线程打印,flag是true");
                break;
            }

        }
    }
}

// 1.实现Runnable
class MemVisia implements Runnable {

    // 使用volatile关键字,就可以解决了
    private volatile boolean flag = false;

    public boolean isflag() {
        return flag;
    }

    public void setflag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 让线程二先进去判断(while判断) 
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }

        // 线程一: 
        flag = true;
        System.out.println("flag: " + flag);
    }
}

结果:
我在主线程打印,flag是true
flag: true


其它



源码下载:

关注下方微信公众号,
回复:
JUC.code

《JUC-001-volatile与内存可见性》

    原文作者:JUC
    原文地址: https://blog.csdn.net/qq_33605778/article/details/78607976
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞