关于java:java并发之volatile

41次阅读

共计 1680 个字符,预计需要花费 5 分钟才能阅读完成。

一、特点

  • 保障可见性:如果变量被 volatile 润饰,那么每次批改之后,接下来在读取这个变量的时候肯定能读取到该变量最新的值。
  • 不保障原子性
  • 禁止指令重排

二、代码

1. 验证 volatile 的可见性
import java.util.concurrent.TimeUnit;

class Share {

    public volatile int num1 = 0;
    public int num2 = 0;

    public void method1() {
        try {TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        this.num1 = 1;
    }
    public void method2() {
        try {TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        this.num2 = 1;
    }
}
public class Solution {public static void main(String[] args) {Share share = new Share();

        // 验证 volatile 的可见性
        new Thread(() -> {share.method1();
        }).start();
        while (share.num1 == 0) { }
        System.out.println("num1 break loop, num1 =" + share.num1);
        
        // 验证非 volatile 的不可见性
        new Thread(() -> {share.method2();
        }).start();
        while (share.num2 == 0) { }
        System.out.println("num2 break loop, num2 =" + share.num2);
    }
}
  • num1 用 volatile 润饰,领有可见性,而 num2 不领有可见性。证实了 volatile 能够保障变量的可见性

外汇名词解释 https://www.fx61.com/definitions

2. 双重测验锁机制的单例模式
class Singleton {

    private static volatile Singleton instance = null;

    private Singleton() {}

    public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 双重测验锁机制的单例模式,线程平安且在多线程状况下能放弃高性能。
  • 留神 Singleton 类的动态变量 instance 应用 volatile 润饰,用于禁止指令重排
//instance = new Singleton(); 分为上面三步执行(伪代码)memory = allocate;//1. 调配对象内存空间
initialization(memory);//2. 初始化对象
instance = memory;//3. 设置 instance 指向刚调配并初始化结束的内存地址,此时 instance != null
  • 步骤 2 和步骤 3 不存在数据依赖关系,而且无论重排前还是重排后程序的执行后果在单线程中并没有扭转,因而这种重排优化是容许的。所以指令重排只会保障串行语义的执行的一致性(单线程),但不会关怀多线程间的语义一致性。
memory = allocate;//1. 调配对象内存空间
instance = memory;//3. 设置 instance 指向刚调配的内存地址,此时 instance != null。initialization(memory);//2. 初始化对象 
  • 所以在执行的时候第二步和第三步齐全有可能颠倒程序(伪代码如上),所以当一个线程拜访 instance 不为 null 时,因为 instance 实例未必曾经初始化实现,也就造成了线程平安问题。所以 instance 变量须要应用 volatile 关键字润饰,用于禁止指令重排

正文完
 0