java 关于单例模式的一点思考

概念
  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
  单例模式有以下特点:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

1.懒汉单例模式

package edu.cczu.singleton;
/**
 * 懒汉式单例类.在第一次调用的时候实例化自己
 * @author 这是一台电脑
 * 
 */
public class SingletonA {
    private SingletonA() {}  
    private static SingletonA instance=null;  
    public static SingletonA getInstance() {  
         if (instance == null) {    
             instance = new SingletonA();  
         }    
        return instance;  
    } 
}

用单元测试来测试下是否能达到单例的效果

@Test
public void test1(){
    SingletonA singletonA_1 = SingletonA.getInstance();
    SingletonA singletonA_2 = SingletonA.getInstance();
    if(singletonA_1 == singletonA_2){
        System.out.println("相同的实例");
    }else{
        System.out.println("不同的实例");
    }
}

输出如下:
这里写图片描述
主要原理就是通过将类的构造器设置为private,这样确保实例唯一。但是通过反射机制就可以打破这一限制了。测试代码如下:

@Test
public void test2() throws Exception{
    Class<?> clazz = Class.forName("edu.cczu.singleton.SingletonA");
    Constructor<?> constructor = clazz.getDeclaredConstructor(null);
    System.out.println(constructor);

    constructor.setAccessible(true);
    SingletonA singletonA_1 = (SingletonA) constructor.newInstance(null);
    SingletonA singletonA_2 = (SingletonA) constructor.newInstance(null);
    if(singletonA_1 == singletonA_2){
        System.out.println("相同的实例");
    }else{
        System.out.println("不同的实例");
    }
}

这里写图片描述
通过反射的setAccessible可以绕过private限制,好吧,这不是今天讨论的重点。
其次,如果是在并发环境下使用getinstance则可能会带来并发问题。

2.线程安全的单例模式

解决并发问题,可以采用以下三种写法:

package edu.cczu.singleton;
/**
 * 懒汉+加锁
 * @author 这是一台电脑
 *
 */
public class SingletonB {
    private SingletonB() {}  
    private static SingletonB instance=null; 

    /**
     * 对getInstance加锁,但实际并发中很少会出现需要加锁的情况,所以不常用
     * 不推荐
     */
    public static synchronized SingletonB getInstance1() {  
        if (instance == null) {    
            instance = new SingletonB();  
        }    
       return instance;  
    } 

    /**
     * 双重判定加锁,同上,做了优化
     */
    public static SingletonB getInstance2() {  
        if (instance == null) {    
            synchronized (SingletonB.class) {    
               if (instance == null) {    
                   instance = new SingletonB();   
               }    
            }    
        }    
        return instance;   
    }


    /**
     * 静态内部类
     * 线程安全,又避免了同步时的性能损耗
     * 推荐使用
     */
    private static class LazyHolder {    
       private static final SingletonB INSTANCE = new SingletonB();    
    }    
    public static final SingletonB getInstance3() {    
       return LazyHolder.INSTANCE;    
    }    

}

3.饿汉单例模式

懒汉模式是在类的getInstance()时才初始化实例,所以会带来线程安全问题,但是饿汉采用的是声明时就初始化实例,所以不会存在线程安全问题,取而代之的是会出现资源浪费的问题。

package edu.cczu.singleton;

public class SingletonC {
    private SingletonC() {}  
    private static final SingletonC instance = new SingletonC();  
    //静态工厂方法   
    public static SingletonC getInstance() {  
        return instance;  
    }  
}

4.枚举类型单例模式

下面是《effective java》的“奇技淫巧”,采用枚举类型,讲道理,是解决了以上所有的问题,就是…并不常用,可能是引入新规范后大家还在沿用原有的习惯吧。

package edu.cczu.singleton;

public enum SingletonD {
    INSTANCE;

    private String somefield;

    public String getSomefield() {
        return somefield;
    }

    public void setSomefield(String somefield) {
        this.somefield = somefield;
    }

    private SingletonD(){}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值