案例一
/**
* volatileTest 第一语义是此关键字保证变量对所有线程具有可见性,指当一条线程修改了此变量的值,新值对于其他线程来说是立即可知的。
* 但该关键字存在不一致的情况,是不安全的。
* 使用该变量一般在符合以下规则:
* |-运算变量并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
* |-变量不需要与其它的状态变量共同参与不变约束。
* @author aiyungui
*
*/
public class VolatileTest {
public static volatile int race = 0;
// public static int race = 0;
/**
* race++看起来是一个最小可执行指令,但编译后,会由多个字节指令组成,那么在执行这些字节指令的同时便会产生线程不安全的情况。
* 一般在这种运算规则中,需要同步同步加锁机制(synchronized或java.until.concurrent中的原子类)
*/
public static void increase(){
race ++;
}
// public synchronized static void increase(){
// race ++;
// }
private static final int THREADS_COUNT = 20;
public static void main(String[] args) {
Thread[] threads = new Thread[THREADS_COUNT];
for (int i = 0; i < THREADS_COUNT; i++) {
threads[i] = new Thread( new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
}
}
});
threads[i].start();
}
while(Thread.activeCount()>1){//返回当前线程的线程组中活动线程的数目。
Thread.yield();//暂停当前正在执行的线程对象,并执行其他线程。 这里是暂停main线程对象
}
System.out.println(race);
}
}
示例二
/**
* VolatileSingleton的第二语义是禁止指令重排序优化
* 重排序优化:从硬件架构上讲,指令重排序是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应的电路单元处理。
* 但不是说指令任意重排,CPU需要能正确处理指令依赖情况以保证程序能得出正确的执行结果。
* @author aiyungui
*
*/
public class VolatileSingleton {
private volatile static VolatileSingleton singleton;
/**
* 在该单例模式的运行中,如没有对singleton用volatile关键字修饰。则当A线程进入顺利进入1,并在2步进行同步锁定,执行3、4。此时B在A执行2、3、4其中之一
* 时依然能顺利进入1,并在2处等待,当A执行完成之后,B再次向下执行,并重新赋值。
* 原因是A线程进入方法时,从主内存读取singleton变量,在并没有把singleton写会主内存之前;B线程从主内存读取了singleton变量,那么此时A\B获取到的singleton变量值
* 都为null,当用volatile修饰变量,在A执行完4后,singleton对其他所有线程都可见,因此B在执行3时,返回false。
* @return
*/
public static VolatileSingleton getInstance(){
if(singleton == null){//1
synchronized (VolatileSingleton.class) {//2
if(singleton == null){//3
singleton = new VolatileSingleton();//4
}
}
}
return singleton;
}
public static void main(String[] args) {
getInstance();
}
}