Java设计模式一 单例模式详解

1.  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

2.  单例模式的特点:

       1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。

  3、单例类必须给所有其他对象提供这一实例。

3.  获取单例的时候,要保证不能产生多个实例对象。 在使用单例对象的时候,要注意单例对象内的实例变量是会被多线程共享的,推荐使用无状态的对象,不会因为多个线程的交替调度而破坏自身状态导致线程安全问题。

    最早使用的MVC框架Struts1中的action就是单例模式的,而到了Struts2就使用了多例。在Struts1里,当有多个请求访问,每个都会分配一个新线程,在这些线程,操作的都是同一个action对象,每个用户的数据都是不同的,而action却只有一个。到了Struts2, action对象为每一个请求产生一个实例,并不会带来线程安全问题。

4.  实现单例模式的方式

1.饿汉式单例(立即加载方式)

// 饿汉式单例   ----线程安全
public class Singleton1 {
    // 私有构造
    private Singleton1() {}

    private static Singleton1 single = new Singleton1();

    // 静态工厂方法
    public static Singleton1 getInstance() {
        return single;
    }
}

饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。(姑且闭着眼就认为反射机制不存在。)

另外一种类初始化时实例化Instance

public class Singleton6 {
    
    // 私有构造
    private Singleton6() {}
    
    private static Singleton6 single = null;

    // 静态代码块
    static{
        single = new Singleton6();
    }
    
    public static Singleton6 getInstance() {
        return single;
    }
}


2.懒汉式单例(延迟加载方式)

// 懒汉式单例  ------非线程安全
public class Singleton2 {

    // 私有构造
    private Singleton2() {}

    private static Singleton2 single = null;

    public static Singleton2 getInstance() {
        if(single == null){
            single = new Singleton2();
        }
        return single;
    }
}

该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个single对象,如何改造请看以下方式:

使用synchronized同步锁

public class Singleton3 {
    // 私有构造
    private Singleton3() {}

    private static Singleton3 single = null;

    public static Singleton3 getInstance() {
        
        // 等同于 public static synchronized  Singleton3 getInstance()
        synchronized(Singleton3.class){
          // 注意:里面的判断是一定要加的,否则出现线程安全问题
            if(single == null){
                single = new Singleton3();
            }
        }
        return single;
    }
}

在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。

public class Singleton4 {
    // 私有构造
    private Singleton4() {}

    private static Singleton4 single = null;

    // 双重检查
    public static Singleton4 getInstance() {
        if (single == null) {
            synchronized (Singleton4.class) {
                if (single == null) {
                    single = new Singleton4();
                }
            }
        }
        return single;
    }
}

使用双重检查进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。


3.静态内部类实现

public class SingletonDemo5 {
    private static class SingletonHolder{
        private static final SingletonDemo5 instance = new SingletonDemo5();
    }
    private SingletonDemo5(){}
    public static final SingletonDemo5 getInsatance(){
        return SingletonHolder.instance;
    }
}


静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉、 懒汉 方式不同的是(很细微的差别):饿汉 、懒汉方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉、 懒汉方法就显得更合理。


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值