【转】23种设计模式之单例模式

CSDN新首页上线啦,邀请你来立即体验!
旧版 立即体验
  • 博客
  • 学院
  • 下载
  • GitChat
  • 更多
    论坛
    问答
    活动
    码云
    商城
    ITeye
    极客头条

JAVA设计模式之单例模式

原创 2014年04月16日 06:51:34
  • 602631
  • 编辑
  • 删除

本文继续介绍23种设计模式系列之单例模式。

概念:
  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
  单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。
  单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


一、懒汉式单例

[java] view plain copy
print ?
  1. //懒汉式单例类.在第一次调用的时候实例化自己   
  2. public class Singleton {  
  3.     private Singleton() {}  
  4.     private static Singleton single=null;  
  5.     //静态工厂方法   
  6.     public static Singleton getInstance() {  
  7.          if (single == null) {    
  8.              single = new Singleton();  
  9.          }    
  10.         return single;  
  11.     }  
  12. }  
//懒汉式单例类.在第一次调用的时候实例化自己 
public class Singleton {
    private Singleton() {}
    private static Singleton single=null;
    //静态工厂方法 
    public static Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
    }
}

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

(事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:


1、在getInstance方法上加同步

[java] view plain copy
print ?
  1. public static synchronized Singleton getInstance() {  
  2.          if (single == null) {    
  3.              single = new Singleton();  
  4.          }    
  5.         return single;  
  6. }  
public static synchronized Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
}

2、双重检查锁定

[java] view plain copy
print ?
  1. public static Singleton getInstance() {  
  2.         if (singleton == null) {    
  3.             synchronized (Singleton.class) {    
  4.                if (singleton == null) {    
  5.                   singleton = new Singleton();   
  6.                }    
  7.             }    
  8.         }    
  9.         return singleton;   
  10.     }  
public static Singleton getInstance() {
        if (singleton == null) {  
            synchronized (Singleton.class) {  
               if (singleton == null) {  
                  singleton = new Singleton(); 
               }  
            }  
        }  
        return singleton; 
    }

3、静态内部类

[java] view plain copy
print ?
  1. public class Singleton {    
  2.     private static class LazyHolder {    
  3.        private static final Singleton INSTANCE = new Singleton();    
  4.     }    
  5.     private Singleton (){}    
  6.     public static final Singleton getInstance() {    
  7.        return LazyHolder.INSTANCE;    
  8.     }    
  9. }    
public class Singleton {  
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。


二、饿汉式单例

[java] view plain copy
print ?
  1. //饿汉式单例类.在类初始化时,已经自行实例化   
  2. public class Singleton1 {  
  3.     private Singleton1() {}  
  4.     private static final Singleton1 single = new Singleton1();  
  5.     //静态工厂方法   
  6.     public static Singleton1 getInstance() {  
  7.         return single;  
  8.     }  
  9. }  
//饿汉式单例类.在类初始化时,已经自行实例化 
public class Singleton1 {
    private Singleton1() {}
    private static final Singleton1 single = new Singleton1();
    //静态工厂方法 
    public static Singleton1 getInstance() {
        return single;
    }
}
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。


三、登记式单例(可忽略)

[java] view plain copy
print ?
  1. //类似Spring里面的方法,将类名注册,下次从里面直接获取。  
  2. public class Singleton3 {  
  3.     private static Map map = new HashMap();  
  4.     static{  
  5.         Singleton3 single = new Singleton3();  
  6.         map.put(single.getClass().getName(), single);  
  7.     }  
  8.     //保护的默认构造子  
  9.     protected Singleton3(){}  
  10.     //静态工厂方法,返还此类惟一的实例  
  11.     public static Singleton3 getInstance(String name) {  
  12.         if(name == null) {  
  13.             name = Singleton3.class.getName();  
  14.             System.out.println("name == null"+"--->name="+name);  
  15.         }  
  16.         if(map.get(name) == null) {  
  17.             try {  
  18.                 map.put(name, (Singleton3) Class.forName(name).newInstance());  
  19.             } catch (InstantiationException e) {  
  20.                 e.printStackTrace();  
  21.             } catch (IllegalAccessException e) {  
  22.                 e.printStackTrace();  
  23.             } catch (ClassNotFoundException e) {  
  24.                 e.printStackTrace();  
  25.             }  
  26.         }  
  27.         return map.get(name);  
  28.     }  
  29.     //一个示意性的商业方法  
  30.     public String about() {      
  31.         return "Hello, I am RegSingleton.";      
  32.     }      
  33.     public static void main(String[] args) {  
  34.         Singleton3 single3 = Singleton3.getInstance(null);  
  35.         System.out.println(single3.about());  
  36.     }  
  37. }  
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3 {
    private static Map map = new HashMap();
    static{
        Singleton3 single = new Singleton3();
        map.put(single.getClass().getName(), single);
    }
    //保护的默认构造子
    protected Singleton3(){}
    //静态工厂方法,返还此类惟一的实例
    public static Singleton3 getInstance(String name) {
        if(name == null) {
            name = Singleton3.class.getName();
            System.out.println("name == null"+"--->name="+name);
        }
        if(map.get(name) == null) {
            try {
                map.put(name, (Singleton3) Class.forName(name).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return map.get(name);
    }
    //一个示意性的商业方法
    public String about() {    
        return "Hello, I am RegSingleton.";    
    }    
    public static void main(String[] args) {
        Singleton3 single3 = Singleton3.getInstance(null);
        System.out.println(single3.about());
    }
}

 登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。


饿汉式和懒汉式区别

从名字上来说,饿汉和懒汉,

饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

另外从以下两点再区分以下这两种方式:


1、线程安全:

饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。



2、资源加载和性能:

饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

至于1、2、3这三种实现又有些区别,

第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。


什么是线程安全?

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。


应用

以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式

[java] view plain copy
print ?
  1. public class TestSingleton {  
  2.     String name = null;  
  3.   
  4.         private TestSingleton() {  
  5.     }  
  6.   
  7.     private static volatile TestSingleton instance = null;  
  8.   
  9.     public static TestSingleton getInstance() {  
  10.            if (instance == null) {    
  11.              synchronized (TestSingleton.class) {    
  12.                 if (instance == null) {    
  13.                    instance = new TestSingleton();   
  14.                 }    
  15.              }    
  16.            }   
  17.            return instance;  
  18.     }  
  19.   
  20.     public String getName() {  
  21.         return name;  
  22.     }  
  23.   
  24.     public void setName(String name) {  
  25.         this.name = name;  
  26.     }  
  27.   
  28.     public void printInfo() {  
  29.         System.out.println("the name is " + name);  
  30.     }  
  31.   
  32. }  
public class TestSingleton {
	String name = null;

        private TestSingleton() {
	}

	private static volatile TestSingleton instance = null;

	public static TestSingleton getInstance() {
           if (instance == null) {  
             synchronized (TestSingleton.class) {  
                if (instance == null) {  
                   instance = new TestSingleton(); 
                }  
             }  
           } 
           return instance;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void printInfo() {
		System.out.println("the name is " + name);
	}

}

可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

还有疑问可参考http://www.iteye.com/topic/652440
和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html


[java] view plain copy
print ?
  1. public class TMain {  
  2.     public static void main(String[] args){  
  3.         TestStream ts1 = TestSingleton.getInstance();  
  4.         ts1.setName("jason");  
  5.         TestStream ts2 = TestSingleton.getInstance();  
  6.         ts2.setName("0539");  
  7.           
  8.         ts1.printInfo();  
  9.         ts2.printInfo();  
  10.           
  11.         if(ts1 == ts2){  
  12.             System.out.println("创建的是同一个实例");  
  13.         }else{  
  14.             System.out.println("创建的不是同一个实例");  
  15.         }  
  16.     }  
  17. }  
public class TMain {
	public static void main(String[] args){
		TestStream ts1 = TestSingleton.getInstance();
		ts1.setName("jason");
		TestStream ts2 = TestSingleton.getInstance();
		ts2.setName("0539");
		
		ts1.printInfo();
		ts2.printInfo();
		
		if(ts1 == ts2){
			System.out.println("创建的是同一个实例");
		}else{
			System.out.println("创建的不是同一个实例");
		}
	}
}

 运行结果:


结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。



更多设计模式:23种设计模式系列


作者:jason0539

博客:http://blog.csdn.net/jason0539(转载请说明出处)


阅读全文
版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 本文已收录于以下专栏:
  • Java设计模式
3_qq_38057418.jpg
HTML/XML objective-c Delphi Ruby PHP C# C++ JavaScript Visual Basic Python Java CSS SQL 其它
Lingchen_xuan
  • Lingchen_xuan

    2017-11-18 12:49 71楼
  • 感谢分享
  • 回复
u010634066
  • u010634066

    2017-10-23 17:44 70楼
  • 延迟初始化占位(Holder)类模式
    说的不是很容易让人理解
    说清楚这个 就要知道 内部类的加载时机;
    懒汉式是 节省内存,不使用的时候不初始化;

    内部类的加载:内部类(不论是静态内部类还是非静态内部类)都是在第一次使用时才会被加载。

    外部类不调用 getInstance()时候 内部类是不会加载的,所以达到了懒汉的效果;
    然后调用的时候 内部类被加载,,加载的时候就会初始化实例;这个加载的过程是不会有多线程的问题的!类加载的时候有一种机制叫做 缓存机制;第一次加载成功之后会被缓存起来;而且一般一个类不会加载多次
  • 回复
zuoside__lord
  • zuoside__lord

    2017-10-13 19:31 69楼
  • 有点不明白,我们书上是最简单的懒汉模式,,,,,暂时不需要思考线程那么远,,,但是还是不是很明白
  • 回复
查看 97 条热评

Java单例模式——并非看起来那么简单

Java中单例(Singleton)模式是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。单例模式有很多好处,它能够避免...
  • goodlixueyong
  • goodlixueyong
  • 2016年07月20日 23:48
  • 22897

7种Java单例模式

单例模式 - 终极篇单例是设计模式当中使用比较常用和重要的一种模式。在应届生笔试过程中,也会被经常要求写两个单例模式。下面是我自己总结的7中单例模式的写法,废话不多说,直接上代码:(一点点敲的,分享...
  • u010923921
  • u010923921
  • 2015年05月05日 17:00
  • 1090
【转】23种设计模式之单例模式_第1张图片 iP0FJ9WyNfNE5tcAAAAASUVORK5CYII=

惊呆了!微博和阿里背后的数据库有多厉害?

想不到!数据库作为最关键的基础设施,渗透技术领域的方方面面,我阿里和微博的师哥们是这么分享的...

Java 中的单例模式,看完这一篇就够了

单例模式是最常见的一个模式,在Java中单例模式被大量的使用。这同样也是我在面试时最喜欢提到的一个面试问题,然后在面试者回答后可以进一步挖掘其细节,这不仅检查了关于单例模式的相关知识,同时也检查了面试...
  • hintcnuie
  • hintcnuie
  • 2014年01月07日 20:25
  • 5866

Java中的单例模式的优秀实现

单例模式不得不说应该算初学者接触的最早几个设计模式之一了,主要是因为它的应用场景比起什么其他模式太简单易懂了,我们都知道,你要一个总体控制的类,比如一个能够初始化功能,提供特定功能的Helper类,那...
  • ll530304349
  • ll530304349
  • 2016年09月21日 15:25
  • 367

Java 单例模式的七种写法

第一种(懒汉,线程不安全):public class Singleton {   2     private static Singleton instance;   3     ...
  • chow__zh
  • chow__zh
  • 2013年05月06日 17:03
  • 3143

java中实现单例模式的几种方式,简单易懂

一、饿汉式单例类public class Singleton { private Singleton(){ } private static Singl...
  • yuxin6866
  • yuxin6866
  • 2016年08月14日 09:39
  • 2399

java单例模式五种实现方式

应用场景由于单例模式只生成一个实例, 减少了系统性能开销(如: 当一个对象的产生需要比较多的资源时, 如读取配置, 产生其他依赖对象, 则可以通过在应用启动时直接产生一个单例对象, 然后永久驻留内存的...
  • JQ_AK47
  • JQ_AK47
  • 2017年02月06日 17:03
  • 2559

单例模式(Java代码实现)

单例模式的定义单例模式确保类只有一个实例,并且提供一个全局的访问点。...
  • sxh850297968
  • sxh850297968
  • 2015年03月20日 16:34
  • 5884

java设计模式之单例模式(几种写法及比较)

概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类只能有一个实例。 ...
  • tolcf
  • tolcf
  • 2015年10月21日 22:56
  • 5948

Java 单例模式探讨

以下是我再次研究单例(java 单例模式缺点)时在网上收集的资料,相信你们看完就对单例完全掌握了...
  • it_man
  • it_man
  • 2010年08月04日 11:31
  • 25632

设计模式学习笔记---单例模式(Java版)

GOF23(Group of  four)创建型模式单例模式,工厂模式,抽象工厂模式,建造者模式,原型模式。结构型模式适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式...
  • scgaliguodong123_
  • scgaliguodong123_
  • 2015年01月09日 09:30
  • 1027

Java五种单例模式与线程安全

转载自:http://blog.chenzuhuang.com/archive/13.html    《设计模式》提出近二十年里,随着面向对象语言发展,单例模式也随之演化,如今其实现形式变得多种多样...
  • janch1
  • janch1
  • 2016年02月25日 16:12
  • 2212

Java设计模式之单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。该类负责创建自己的对象,同时确保只有单个对象被创建...
  • LOVELONG8808
  • LOVELONG8808
  • 2017年11月27日 11:31
  • 29

JAVA设计模式--单例模式

单例模式三个关键点:
  • hai_cheng001
  • hai_cheng001
  • 2014年09月03日 00:07
  • 1618

设计模式(二)单例模式的七种写法

面试的时候,问到许多年轻的Android开发他所会的设计模式是什么,基本上都会提到单例模式,但是对单例模式也是一知半解,在Android开发中我们经常会运用单例模式,所以我们还是要更了解单例模式才对。...
  • itachi85
  • itachi85
  • 2016年01月17日 10:29
  • 23002

C++中的单例模式

单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,G...
  • Hackbuteer1
  • Hackbuteer1
  • 2012年04月14日 10:08
  • 169357

单例模式(Singleton)- 最易懂的设计模式解析

前言今天我来全面总结一下Android开发中最常用的设计模式 - 单例模式。 关于设计模式的介绍,可以看下我之前写的:1分钟全面了解“设计模式”目录1. 引入1.1 解决的是什么问题之前说过,设...
  • carson_ho
  • carson_ho
  • 2016年08月16日 17:15
  • 3833

(一)单例模式详解

作者:zuoxiaolong8810(左潇龙),转载请注明出处。            上一章,我们学习了设计模式的概念,以及为什么要学习设计模式,还有在进行系统设计时应当遵守的六大原则,本章我们就...
  • zuoxiaolong8810
  • zuoxiaolong8810
  • 2013年06月02日 13:15
  • 5762

单例模式

单例模式单例模式是最简单的一种模式了相对于其他模式,他的主要目的就是为了保证一个类只能有一个实例。这在编程当中也是非常常见的。比方说window任务管理器,就只能开一个,比方说我们开发的时候弹出的窗...
  • VictoryRSS
  • VictoryRSS
  • 2017年11月14日 10:47
  • 39

23种设计模式(1):单例模式

定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。类型:创建类模式类图:类图知识点:1.类图分为三部分,依次是类名、属性、方法2.以>结尾的为注释信息3...
  • zhengzhb
  • zhengzhb
  • 2012年03月08日 09:12
  • 107073

相关推荐

  • Java单例模式——并非看起来那么简单
  • 7种Java单例模式
  • Java 中的单例模式,看完这一篇就够了
  • Java中的单例模式的优秀实现
iP0FJ9WyNfNE5tcAAAAASUVORK5CYII=

博主专栏

  • 【转】23种设计模式之单例模式_第2张图片
    65

    android之开发技术详解

    1021745
  • 【转】23种设计模式之单例模式_第3张图片
    9

    每天一道算法

    26533
  • 【转】23种设计模式之单例模式_第4张图片
    21

    Java设计模式

    1346646
iP0FJ9WyNfNE5tcAAAAASUVORK5CYII=

他的热门文章

  • JAVA设计模式之单例模式
    601656
  • JAVA设计模式之工厂模式(简单工厂模式+工厂方法模式)
    209086
  • android之存储篇_SQLite数据库_让你彻底学会SQLite的使用
    171282
  • android之switch控件的用法
    113573
  • java可视化编程-eclipse安装windowbuilder插件
    106880

关注我

【转】23种设计模式之单例模式_第5张图片
扫码关注公众号
点击关注微博
不良信息举报
您举报文章:JAVA设计模式之单例模式
举报原因: 色情 政治 抄袭 广告 招聘 骂人
其他
原因补充:

(最多只允许输入30个字)

btn_cancel.jpg

你可能感兴趣的:(【转】23种设计模式之单例模式)