JAVA单例类线程安全问题

sky_fly
  • 1
新手上路,请多包涵

如果一个单例类的变量 只有一个线程A进行修改 其他B C D线程对其进行读取 需要加上Volatile保证其可见性吗?

回复
阅读 769
3 个回答

一般,最好的单例模式是枚举,这是effective java 中的推荐。
如果枚举没办法满足要求,则推荐延迟初始化占位符这种方式。更加清晰,明了。DCL那种方式容易让人犯错。

public class Singleton {
    
    private Singleton(){}
    /**
     *    类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     *    没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
     */
    private static class SingletonHolder{
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

需要,必要的话上读写锁

如果你的目的是读写不冲突,那么最好的解决方法就是加上读写锁,至于加不加Valotile跟这个无关,就算你加上Valotile也只是防止指令重排序而已。
那么什么时候会用到这个Valotile呢?当你对并发要求十分高的时候,什么意思呢?
比如你使用双重检查锁的时候,现在有两个线程,其中一个线程拿到锁开始实例变量的初始化过程。
假设这个初始化过程分为好几步,由于CPU的重排序效果,导致生成实例变量的塞入内存时(其中一步)还没有完全初始化完成(其他几步也参与完成初始化)。
那么此时线程二进入获取实例变量的方法,首先判断实例变量是否为空的返回值将是false,那么它会尝试去内存中拿到这个变量,正如上文所说的的,它拿到的这个变量将是一个中间的状态,因此会出现问题。
为了避免该问题,就需要加上Volotile来防止指令重排序,使初始化过程按步骤进行,内存中的内容一定不是一个中间状态的结果。
但还是那句话,我们真的需要这么极致的防高并发操作么?我想这种情况很难出现。。。所以很多单例的实现中并没有提到这一点。

宣传栏