你说一下对Java中的volatile的理解吧 (3)

要保证累加最终结果正确,要么对累加变量加锁,要么就用AotomicInteger这样的变量。

/** * 双重检查加锁式单例 */ public class DoubleCheckLockSingleton implements Serializable{ /** * 静态变量,用来存放实例。 */ private volatile static DoubleCheckLockSingleton doubleCheckLockSingleton = null; /** * 私有化构造方法,禁止外部创建实例。 */ private DoubleCheckLockSingleton(){} /** * 双重检查加锁的方式保证线程安全又能获得到唯一实例 * @return */ public static DoubleCheckLockSingleton getInstance(){ //第一次检查实例是否已经存在,不存在则进入代码块 if(null == doubleCheckLockSingleton){ synchronized (DoubleCheckLockSingleton.class){ //第二次检查 if(null==doubleCheckLockSingleton){ doubleCheckLockSingleton = new DoubleCheckLockSingleton(); } } } return doubleCheckLockSingleton; } }

为什么要进行双重检查呢?
当第一个线程走到第一次检查时发现对象为空,然后进入锁,第二次就检查时也为空,那么就去创建对象,但是这个时候又来了一个线程来到了第一次检查,发现为空,但是这个时候因为锁被占用,所以就只能阻塞等待,然后第一个线程创建对象成功了,由于对象是被volatile修饰的能够立即反馈到其他线程上,所以在第一个线程释放锁之后,第二个线程进入了锁,然后进行第二次检查时,发现对象已经被创建了,那么就不在创建对象了。从而保证的单例。

还有就是如果创建对象,步骤:

分配内存空间。

调用构造器,实例化。

返回内存地址给引用。

如果这三个指令顺序被重排了,那么当多线程来获取对象的时候就会造成对象虽然实例化了,但是没有分配内存空间,会有空指针的风险。
所以加上了volatile的对象,也保证了在第二次检查时不会被已经在创建过程中的对象有被检测为空的风险。

总结一下

volatile其实可以看作是轻量级的synchronized,虽然说volatile不能保证原子性,但是如果在多线程下的操作本身就是原子性操作(例如赋值操作),那么使用volatile会由于synchronized。

volatile可以适用于,某个标识flag,一旦被修改了就需要被其他线程立即可见的情况。也可以修饰作为触发器的变量,一旦变量被任何一个线程修改了,就去触发执行某个操作。

volatile的变量写操作happen-before,后面任何对此volatile变量的读操作。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxzjj.html