多线程学习笔记(三)之单例模式中的线程问题

在某些情况下,每个类只需要一个实例,单例模式就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在),核心点:

  • 将采用单例模式的类的构造方法私有化(private修饰)
  • 在其内部产生该类的实例化对象,并将其封装成private static 类型
  • 定义一个public静态方法返回该实例

饿汉式

优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;
缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。

class Single{
    private static final Single s = new Single();
    private Single(){}
    public static Single getInstance(){
        return s;
    }
}

当多线程时,s为共享数据,不会存在安全问题,因为static final类型,每次返回的都是同一个地址。

饱汉式

优点是:写起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存;
缺点是:并发环境下很可能出现多个SingletonTest实例。

class SingleT{
    private static SingleT s = null;
    private SingleT(){}
    public static SingleT getInstance(){
        if (s==null)
            s = new SingleT();
        return s;
    }
}

线程0进入在验证s==null后切换到线程1,同样在执行s==null语句后切换,将会导致0与1线程都会执行s = new SingleT()语句,返回的是两个对象,而非单例

修改方式一:

优点是:使用synchronized关键字避免多线程访问时,出现多个SingletonTest实例。
缺点是:同步方法频繁调用时,效率略低。

class SingleT{
    private static SingleT s = null;
    private SingleT(){}
    public static synchronized SingleT getInstance(){
        if (s==null)
            s = new SingleT();
        return s;
    }
}

每次线程在执行s==null判断,获得对象之前,都需要判断锁,因此效率较低

修改方式二

最佳实现,内存占用低,效率高,并且保证线程安全。

class SingleT{
    private static SingleT s = null;
    private SingleT(){}
    public static SingleT getInstance(){
        //注意,此处是静态static方法,因此同步锁是类的字节码,而不可以是this
        //此处使用“类名.class”的方式,不可以使用getClass方法,因为getClass方法是非静态的,而此处是静态方法中
        //此处的if判断语句是为了解决效率问题,当s非空时,直接跳过以下语句的执行
        if (s==null){
            //加锁是为了解决安全问题
            synchronized(SingleT.class){
                if (s==null)
                    s = new SingleT();
            }
        }
        return s;
    }
}

你可能感兴趣的:(并发)