您的位置:首页 > 其它

单例模式详解

2016-03-31 15:44 417 查看
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。在多线程更是要注意。

参考:

JAVA设计模式之单例模式

单例模式与双重检测

The “Double-Checked Locking is Broken” Declaration

Java多线程编程环境中单例模式的实现

单例模式的特点:

  1、单例类只能有一个实例。

  2、单例类必须自己创建自己的唯一实例。

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

懒汉式单例

/**
* 懒汉式,在第一次调用的时候实例化自己<p>
*  本身是线程不安全的,并发环境下很可能出现多个Singleton实例。
*  如果在静态工厂上加了同步,虽然线程安全了,但是每次都要同步,
*   会影响性能,毕竟99%的情况下是不需要同步的
*/
public class LazySingleton{
private static LazySingleton singleton = null;

private LazySingleton(){
}
//静态工厂方法 。此方法设置成 静态同步方法就可线程安全
//  public static synchronized LazySingleton getInstance(){
public static LazySingleton getInstance(){
if(singleton == null){
singleton = new LazySingleton();
}
return singleton;
}
/*
//另外一种方法(双重检查锁定)
public static LazySingleton getInstance(){
if(singleton == null){
synchronized (LazySingleton.class) {
if(singleton == null){
singleton = new LazySingleton();
}
}
}
return singleton;
}
*/
}


Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例。

要实现线程安全可以对getInstance这个方法改造,保证了懒汉式单例的线程安全。

1.将getInstance()方法设置成 静态同步方法就可线程安全。

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


这种办法很影响性能:每次调用getInstance方法的时候都必须获得Singleton的锁,而实际上,当单例实例被创建以后,其后的请求没有必要再使用互斥机制了

2.将getInstance()方法内部添加双重检查锁定

public static LazySingleton getInstance(){
if(singleton == null){
//注意这里不能写 synchronized(this) 因为这是静态方法内部,this从哪里来
synchronized (LazySingleton.class) {
if(singleton == null){
singleton = new LazySingleton();
}
}
}
return singleton;
}


*有出错成的可能,以A、B两个线程为例:

A、B线程同时进入了第一个if判断

A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();

由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

*

饿汉式单例

/**
* 饿汉式<p>
* 饿汉式单例类.在类初始化时,已经自行实例化
* 饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
*/
public class HungrySingleton {
private static HungrySingleton singleton = new HungrySingleton();

private HungrySingleton(){
}

public static HungrySingleton getInstance(){
return singleton;
}
}


这样的代码缺点是:第一次加载类的时候会连带着创建Singleton实例,这样的结果与我们所期望的不同,因为创建实例的时候可能并不是我们需要这个实例的时候。同时如果这个Singleton实例的创建非常消耗系统资源,而应用始终都没有使用Singleton实例,那么创建Singleton消耗的系统资源就被白白浪费了。

静态内部类式

/**
* 静态内部类<p>
* 相比懒汉式,实现了线程安全,又避免了同步带来的性能影响
*/
public class InternalSingleton {
private InternalSingleton(){}

public static InternalSingleton getInstance(){
return SingletonHolder.INSTANCE;
}

private static class SingletonHolder{
private final static InternalSingleton INSTANCE = new InternalSingleton();
}
}


JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕。此外该方法也只会在第一次调用的时候使用互斥机制,这样就解决了”静态同步方法”的问题。最后instance是在第一次加载SingletonContainer类时被创建的,而SingletonContainer类则在调用getInstance方法的时候才会被加载,因此也实现了惰性加载。

测试:

import org.junit.Test;

public class GetSingletonInstance {
@Test
public void getLazySingletonInstance() {
LazySingleton singleton1 = LazySingleton.getInstance();
LazySingleton singleton2 = LazySingleton.getInstance();
if(singleton1 == singleton2){
System.out.println("是同一个实例!");
}else{
System.out.println("不是同一个实例!");
}
}

@Test
public void getHungrySingletonInstance() {
HungrySingleton singleton1 = HungrySingleton.getInstance();
HungrySingleton singleton2 = HungrySingleton.getInstance();
if(singleton1 == singleton2){
System.out.println("是同一个实例!");
}else{
System.out.println("不是同一个实例!");
}
}

@Test
public void getInternalSingletonInstance() {
InternalSingleton singleton1 = InternalSingleton.getInstance();
InternalSingleton singleton2 = InternalSingleton.getInstance();
if(singleton1 == singleton2){
System.out.println("是同一个实例!");
}else{
System.out.println("不是同一个实例!");
}
}
}


结果:

是同一个实例!
是同一个实例!
是同一个实例!


结论

优先使用 静态内部类式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  单例模式 singleton