java学习笔记: volatile 解决内存可见性问题

 

1、Java内存模型

        java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

2、Java中的可见性

        对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

3、volatile 关键字

       Java 提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同:a:对于多线程,不是一种互斥关系;b:不能保证变量状态的“原子性操作”。

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
        while (true){
                if (myRunnable.getFlag()) {
                    System.out.println("进来了");
                    break;
            }

        }
    }
}

class MyRunnable implements Runnable{
    private volatile boolean flag;
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag=true;
        System.out.println("子线程修改了flag的值"+getFlag());
    }
    public boolean getFlag() {
        return flag;
    }
}

4、CAS 算法

       CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问。CAS 是一种无锁的非阻塞算法的实现。CAS 包含了 3 个操作数:a:需要读写的内存值 V;b:进行比较的值 A;c:拟写入的新值 B。当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作。
      简单的来说,CAS有3个操作数,要读写的内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它。

5、原子性问题

    在数据库中事务必须要有原子性,它所做的对数据改操作要全部执行,要么全部不执行。此时的原子性是相同的概念。 可以看一下i++的原子性问题。

//i++的操作实际上分为三个步骤”读-改-写”。
int i = 10;
i = i++; //10
//i++,实际上执行了下面三步:
int temp = i;
i = i + 1;
i = temp;
//只有这三步同时执行成功或失败,就是一个原子操作。

      java.util.concurrent.atomic 这个包下,有很多原子变量供我们去使用。JDK1.5之后给我们提供了一些原子变量,实现了CAS算法,使用这些原子变量,就可以解决这个问题 

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 10; i++) {
            new Thread(myRunnable).start();
        }
        //我们采用同步来解决,可以,但是比较耗费性能
        //现在有一种这个CAS 算法可以解决原子性问题
        // CSA 比较并交换  是一种硬件对并发访问的支持
    }
}

class MyRunnable implements Runnable {
    //int i = 0;//普通变量
    AtomicInteger i = new AtomicInteger(); //原子变量


    public int getI() {
        return i.getAndIncrement();
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "====" + getI());
        }
    }
}
发布了24 篇原创文章 · 获赞 11 · 访问量 2048

猜你喜欢

转载自blog.csdn.net/weixin_43791069/article/details/86696065
今日推荐