美文网首页
14_Volatile关键字

14_Volatile关键字

作者: 真是个点子王 | 来源:发表于2020-12-16 16:06 被阅读0次

多线程访问变量的不可见性

  • 多个线程访问共享变量,会出现一个线程修改变量的值后,其他线程看不到变量最新值的情况。
public class VolatileDemo01 {
    public  static void main(String[] args) {
        // 1.启动线程,把线程对象中的flag改为true。
        VolatileThread t = new VolatileThread();
        t.start();

        // 2.定义一个死循环
        while(true){
            // 这里读取到了flag值一直是false,虽然线程已经把它的值改成了true。
            if(t.isFlag()){
                System.out.println("执行了循环一次~~~~~~~");
            }
        }
      
    }
}
// 线程类。
class VolatileThread extends Thread {
    private  boolean flag = false ;
    public boolean isFlag() {
        return flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 将flag的值更改为true
        this.flag = true ;
        System.out.println("线程修改了flag=" + flag);
    }
}
  • 如上代码,尽管在子线程中,已经对flag的值进行过修改,但是这个修改对于主线程来说是不可见的。

不可见性的原因

  • JMM(Java Memory Model)Java虚拟机规范中所定义的一种内存模型;
  • Java内存模型描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存中读取变量这样的底层细节
  • 所有共享变量都存储在主内存。
  • 线程的工作内存,保留了被线程使用的变量的工作副本。线程对变量所有的操作都必须在工作内存中完成,而不能直接读写主内存中变量。不同线程之间也不能直接访问。


不可见性的解决方法

  • 1.加锁
    • 加锁可以实现其他线程对于变量修改的可见性
    • 原因:某一个线程进入synchronized代码块前后会有如下操作 :
      • 线程获得锁
      • 清空工作区内存
      • 从主内存拷贝共享变量的最新值到工作内存作为副本
      • 执行代码
      • 将修改后的副本的值刷新回主内存中
      • 线程释放锁
public class Mytest1 {
    public  static void main(String[] args) {
        // 1.启动线程,把线程对象中的flag改为true。
        VolatileThread t = new VolatileThread();
        t.start();

        // 2.定义一个死循环
        while(true){
            // 这里读取到了flag值一直是false,虽然线程已经把它的值改成了true。
            synchronized (Mytest1.class){
                if(t.isFlag()){
                    System.out.println("执行了循环一次~~~~~~~");
                }
            }
        }
    }
}

// 线程类。
class VolatileThread extends Thread {
    private  boolean flag = false ;
    public boolean isFlag() {
        return flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 将flag的值更改为true
        this.flag = true ;
        System.out.println("线程修改了flag=" + flag);
    }
}
  • 2.使用volatile关键字
    • 可以给成员变量加上volatile关键字,当一个线程修改了这个成员变量的值,其他线程可以立即查看到修改后的值并使用。
  • synchronizedvolatile 区别
    • volatile只能修饰实例变量和类变量,而synchronized可以修饰方法,以及代码块;
    • volatile保证数据的可见性,但是不保证数据的原子性,而synchronized是一种排他的机制。

原子性

  • 原子性是指在一次操作或者多次操作中,所有的操作全部都得到了执行并且不会受到任何因素的干扰。
  • 原子性问题实例
public class VolatileAtomicThread implements Runnable {
    // 定义一个int类型的遍历
    private volatile int count = 0 ;
    @Override
    public void run() {
        // 对该变量进行++操作,100次
        for(int x = 0 ; x < 100 ; x++) {
            count++ ;                   
            System.out.println("count =========>>>> " + count);
        }
    }
}

class VolatileAtomicThreadDemo {
    public static void main(String[] args) {
        // 创建VolatileAtomicThread对象
        Runnable target = new VolatileAtomicThread() ;
        // 开启100个线程对执行这一个任务。
        for(int x = 0 ; x < 100 ; x++) {
            new Thread(target).start();
        }
    }
}
  • 原子性问题解决方案:
    • 1.加锁,但是加锁机制性能较差
    • 2.使用原子类

原子类

  • Java已经提供了一些本身即可实现原子性(线程安全)的类
  • JavaJDK 1.5开始提供了java.util.concurrent.atomic
  • 例如对于整型操作的原子类
  public AtomicInteger():                   初始化一个默认值为0的原子型Integer
  public AtomicInteger(int initialValue): 初始化一个指定值的原子型Integer
  int get():                             获取值
  int getAndIncrement():                 以原子方式将当前值加1,注意,这里返回的是自增前的值。
  int incrementAndGet():                 以原子方式将当前值加1,注意,这里返回的是自增后的值。
  int addAndGet(int data):               以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
  int getAndSet(int value):              以原子方式设置为newValue的值,并返回旧值。
public class VolatileAtomicThread implements Runnable {
    // 原子类中封装好了整型变量,默认值是0
    private AtomicInteger atomicInteger = new AtomicInteger();
    @Override
    public void run() {
        // 对该变量进行++操作,100次
        for(int x = 0 ; x < 100 ; x++) {
            int count = atomicInteger.incrementAndGet(); // 底层变量+1且返回!
            System.out.println("count =========>>>> " + count);
        }
    }
}

class VolatileAtomicThreadDemo {
    public static void main(String[] args) {
        // 创建VolatileAtomicThread对象
        Runnable target = new VolatileAtomicThread() ;
        // 开启100个线程对执行这一个任务。
        for(int x = 0 ; x < 100 ; x++) {
            new Thread(target).start();
        }
    }
}

相关文章

网友评论

      本文标题:14_Volatile关键字

      本文链接:https://www.haomeiwen.com/subject/xcxogktx.html