您的位置:首页 > 编程语言 > Java开发

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

2016-04-11 16:08 567 查看
概念

  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(){}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: