Java设计模式之单例模式

Java设计模式之懒汉单例模式

 

//懒汉式单例模式
public class LazySingleton {
    private static LazySingleton lazySingleton=null;
    
    //私有构造,不允许外面 new 对象
    private LazySingleton(){}

    public static LazySingleton getInstance(){
        if (lazySingleton==null){
            lazySingleton=new LazySingleton();
        }
        return lazySingleton;
    }
}

//新建一个Runnable
public class T implements Runnable {
    @Override
    public void run() {
        LazySingleton lazySingleton=LazySingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+lazySingleton);
    }
}

//调用
 public static void main(String[] a){
        Thread t1=new Thread(new T());
        Thread t2=new Thread(new T());
        t1.start();
        t2.start();
    }

//结果:返回的是同一个实例。
Thread-1com.zk.javatest.singleton.lazy_singleton.LazySingleton@f63ae59
Thread-0com.zk.javatest.singleton.lazy_singleton.LazySingleton@f63ae59


这里如果是单线程调用没有问题,如果是多线程调用,则存在隐患。 

//多线程时,这段代码可能被多次执行。 
if (lazySingleton==null){
            lazySingleton=new LazySingleton();
        }

下面我们调试一下多线程, 右击红色断点,Suspend方式选择Thread,则可进行多线程调试。

 

上面调试多线程可以看到,LazySingleton创建了两次,因此多线程时存在的这种隐患,既可能导致资源浪费,也违背了单例原则。

解决这个问题的方法也很简单,在静态方法那里加同步锁即可。

public class LazySingleton {
    private static LazySingleton lazySingleton=null;
    private LazySingleton(){}

    //增加同步锁synchronized即可解决多线程问题。
    public synchronized static LazySingleton getInstance(){
        if (lazySingleton==null){
            lazySingleton=new LazySingleton();
        }
        return lazySingleton;
    }
}

 增加同步锁虽然解决了上面的问题,但是带来了新的问题,同步锁需要加锁和解锁,这会增加开销,而且锁的范围比较大,影响性能。

为了解决同步锁带来的问题,采用双检查机制:

//懒汉单例模式之双检查
public class LazyDoubleCheckSingleton {
    private static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;
    private LazyDoubleCheckSingleton(){}
    public static LazyDoubleCheckSingleton getInstance(){
        if (lazyDoubleCheckSingleton==null){
            
            //在里面加锁,缩小加锁区域
            synchronized(LazyDoubleCheckSingleton.class){
                //双检查
                if (lazyDoubleCheckSingleton==null){
                    lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

好了,到这貌似一切都完美了。是吗?事实上并没有,上面的双检查机制也有一个问题,重排序。


lazyDoubleCheckSingleton=new LazyDoubleCheckSingleton();

上面这行代码实际上有三步:
1.给对象分配内存空间;
2.初始化对象;
3.让对象指向已分配的内存空间;

重排序:为什么有重排序?系统为了提高性能,可能会重排序。即:
1.给对象分配内存空间;
3.让对象指向已分配的内存空间;(执行完这步后,lazyDoubleCheckSingleton对象不为null了)
2.初始化对象;

所以,多线程时就可能存在访问未初始化的对象lazyDoubleCheckSingleton,导致报错。

第一种解决方法就是使用 volatile 关键字来禁止重排序 。

    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton;

 第二种解决方式就是采用静态内部类。

多线程时,某个线程获取到初始化对象锁之后,初始化对象时即使重排序了,由于加了锁,其他线程无法并行对同一对象进行初始化,因此可以解决上面的问题。

//静态内部类的单例模式
public class InnerSingleton {
    private InnerSingleton(){}

    private static class InnerClass{
        private static InnerSingleton innerSingleton=new InnerSingleton();
    }

    public static InnerSingleton getInstance(){
        return InnerClass.innerSingleton;
    }

}

//调用
    InnerSingleton innerClass=InnerSingleton.getInstance();

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值