volatile关键字

1. 简介

为了保证线程的安全性问题,Java语言提供了两种关键字volatile 和 synchronized关键字。

  1. volatile关键字保证:可见性,有序性(禁止指令重排序)。可以

  2. synchronized关键字保证:可见性,原子性,有序性。

其中volatile并没有解决原子性的问题,对于一些原子操作volatile修饰的变量还是无法保证线程的安全性问题,这时就必须使用synchronized关键字或者其他同步手段。


2. 可见性

  1. 先来看一下多线程的工作模型,线程工作时每个线程都有各自的工作缓存区,当线程需要对变量操纵时,先从主内存中将数据读到各自的缓存中,再将缓存中的数据调入线程主体完成对变量的操作,然后将数据写入到缓存中,最后回写到主内存中。由于线程之间的并发是存有不确定因素的,可能线程1读取到缓存1之后,线程2也将数据读到缓存2中并且完成了对数据的修改回写到主内存中,此时缓存1中的数据就是一个脏数据,并没有什么用。这时就需要一种手段,线程1修改完数据线程2能立马知道这就是可见性。

在这里插入图片描述

class RunThread1 extends Thread{
    public boolean is = true;

    @Override
    public void run() {
        System.out.println("进入run方法!");
        while(is){ }
        System.out.println("线程被停止了!");
    }
}

public class Demo01 {
    public static void main(String[] args) {
        try{
            RunThread1 r = new RunThread1();
            r.start();
            Thread.sleep(1000);
            r.is = false;
            System.out.println("赋值为false!");

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
注意箭头,程序并没有终止运行。


  1. volatile修饰的变量,具有可见性。强制线程主体直接从主内存中读取数据。越过各自的缓存,这样就能保证每次拿到的都是最新的数据。

在这里插入图片描述

class RunThread2 extends Thread{
    volatile public boolean is = true;

    @Override
    public void run() {
        System.out.println("进入run方法!");
        while(is){
        }
        System.out.println("线程被停止了!");
    }
}

public class Demo02 {
    public static void main(String[] args) {
        try {
            RunThread2 r = new RunThread2();
            r.start();
            Thread.sleep(1000);
            r.is = false;
            System.out.println("赋值为false!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述



3. 原子性

volatile并没有解决原子性的问题,什么是原子性?

原子:原子是化学反应不可再分的最小微粒,也是构成一般物质的最小单位。

原子性:具有不可再分割性。比如常见的变量操作 i++ 其实是分为3步曲,从内存中取值,i = i + 1,然后回写到内存中,至少需要经历三个步骤。在多线程并发状态下volatile 修饰的变量是无法保证线程安全的。i = i + 1有可能多个线程同时操作,一旦一个线程完成了修改其他线程拿到的依然是脏数据。这时需要synchronized同步 或者 使用volatile + Atomic类来保证了,Atomic类的虽然能够完成但是计算的中间过程是不对的。

  1. 只使用volatile修饰变量。
class MyRun1 implements Runnable{
    volatile int a = 0;
    @Override
    public void run() {
        for(int i = 0;i < 1000;i++){
            a++;
        }
    }
}

public class Demo01 {
    public static void main(String[] args) {
        try {
            MyRun1 r = new MyRun1();
            Thread [] a = new Thread[10];
            for(int i = 0;i < a.length;i++){
                a[i] = new Thread(r);
                a[i].start();
            }
            Thread.sleep(2000);
            System.out.println(r.a);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

  1. 使用volatile修饰变量 + AtomicInteger类。
import java.util.concurrent.atomic.*;

class MyRun2 implements Runnable{
    volatile AtomicInteger a = new AtomicInteger(0);
    @Override
    public void run() {
        for(int i = 0;i < 1000;i++){
            a.getAndIncrement();
        }
        System.out.println(Thread.currentThread().getName() + "线程加完100后的值: " + a.toString());
    }
}

public class Demo02 {
    public static void main(String[] args) {
        try {
            MyRun2 r = new MyRun2();
            Thread [] a = new Thread[10];
            for(int i = 0;i < a.length;i++){
                a[i] = new Thread(r);
                a[i].start();
            }
            Thread.sleep(2000);
            System.out.println(r.a);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

在这里插入图片描述


4. 有序性(禁止指令重排序)

  1. 重排序:在Java程序运行的时候,即时编译器对没有拓扑依赖关系的指令,动态的改变程序代码的运行的顺序。可以提高运行的效率。

在这里插入图片描述
当然也有可能BD互换,AC互换也是有可能的。

public class Demo01 {
    private static int x = 0;
    private static int y = 0;
    private static int a = 0;
    private static int b = 0;
    private static int c = 0;
    private static int d = 0;
    private static int e = 0;
    private static int f = 0;

    private static int cnt = 0;
    public static void main(String[] args) throws InterruptedException {
        while(true){
            x = y = a = b = c = d = e = f = 0;
            cnt++;
            Thread t1 = new Thread( () -> {
                a = 1;
                c = 5;
                d = 8;
                x = b;
            });
            Thread t2 = new Thread( () -> {
                b = 1;
                e = 8;
                f = 7;
                y = a;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String s = "cnt = " + cnt + ", x = " + x + ", y = " + y;
            System.out.println(s);
            if(x == 0 && y == 0){
                break;
            }
        }
    }
}

在这里插入图片描述

  1. volatile关键字解决了有序性的问题可以禁止代码指令的重排序,当然并不是指所有的指令重排序。而是以volatile关键字为界限,上界没有拓扑依赖的指令可以重排序,下界没有拓扑依赖的指令也可以重排序。

在这里插入图片描述

public class Demo02 {
    private static int x = 0;
    private static int y = 0;
    private static int a = 0;
    private static int b = 0;
    volatile private static int c = 0;
    private static int d = 0;
    private static int e = 0;
    private static int f = 0;

    private static int cnt = 0;
    public static void main(String[] args) throws InterruptedException {
        while(true){
            x = y = a = b = c = d = e = f = 0;
            cnt++;
            Thread t1 = new Thread( () -> {
                c = 7;
                a = 1;
                d = 8;
                x = b;
            });
            Thread t2 = new Thread( () -> {
                e = d;
                b = 1;
                f = d;
                y = a;
            });
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            String s = "cnt = " + cnt + ", x = " + x + ", y = " + y + ", e = " + e + ", f = " + f;
            System.out.println(s);
            if(x == 0 && y == 0 && e == 0 && f == 0){
                break;
            }
        }
    }
}

在这里插入图片描述



5. 总结

  1. 可见性:volatile关键字 和 synchronized关键字都实现了可见性。

  2. 原子性:只有synchronized实现了原子性

  3. 禁止指令重排序:volatile关键字 和 synchronized关键字保证了有序性。

  4. volatile关键字是轻量级的同步,synchronized关键字重量级的同步。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值