Java设计模式—Singleton

单例模式的特点:

  1、单例模式只能有一个实例

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

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

  概述:保证一个类,只有一个实例存在,同时提供对该实例加以访问的全局访问方法。

 

单例模式的运用:线程池,缓存,日志对象,对话框,打印机,显卡驱动常常被设计为单例模式。主要就是避免不一致状态。

 

单例模式的实现方式:

  懒汉式

  饿汉式

  双重检查

 

 

一、懒汉式

 

 1 package cn.zpoor.singletonBlog;
 2 //懒汉式单例模式,在第一次调用的时候实例化自己
 3 public class Singleton {
 4     private static Singleton singleton = null;
 5     private Singleton() {}
 6     
 7     //实现全局方法(静态工厂方法)
 8     public static Singleton getSingleton() {
 9         if (singleton == null) {
10             singleton = new Singleton();
11         }
12         return singleton;
13     }
14 }

 

 

解释:

  私有化的构造方法,避免在外部类实例化,Singleton的唯一实例只能通过getSingleton()方法访问。

  但是懒汉式单例没有考虑线程的安全,多线程环境下可能会出现多个Singleton实例,实现线程安全有多种方法。举个栗子

 

1、在getSingleto()方法加上同步锁

1 public static synchronized Singleton getSingleton() {
2         if (singleton == null) {
3             singleton = new Singleton();
4         }
5         return singleton;
6     }

 

 

2、双重检查

 

 1 public static Singleton getSingleton() {
 2         if (singleton == null) {
 3             synchronized (Singleton.class) {
 4                 if (singleton == null) {
 5                     singleton = new Singleton();
 6                 }
 7             }
 8         }
 9         return singleton;
10     }

 

 

3、静态内部类(实现了线程安全,又避免同步带来的性能影响)

 1 public class Singleton {
 2     public static class Lazy {
 3         private static final Singleton INSTANCE = new Singleton();
 4     }
 5     
 6     private Singleton() {
 7         
 8     }
 9     
10     public static final Singleton getSingleton() {
11         return Lazy.INSTANCE;
12     }
13 }

 

 

二、饿汉式

 1 package cn.zpoor.singletonBlog;
 2 //饿汉式单例,在类初始化时,已经自行初始化
 3 public class Singleton {
 4     private static final Singleton SINGLETON = new Singleton();
 5     
 6     private Singleton() {
 7         
 8     } 
 9     
10     //静态全局方法
11     public static Singleton getSingleton () {
12         return SINGLETON;
13     }
14 }

在类创建的时候就已经创建好一个静态的对象提供给系统使用,不在改变,所以线程是安全的。

 

 

懒汉和饿汉的区别:

  字面上一个懒一个饿。

  饿汉:类一旦初始化,单例初始化完成,调用getSingleton的时候,单例已经存在

  懒汉:只有在调用getSingleton的时候,才会去初始化这个单例

 

从线程安全上面:

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

  懒汉式本身是非线程安全的,实现线程安全一般是加同步锁、双重检查,静态内部类

 

从资源加载和性能上面:

  饿汉式在类创建的时候就会实例化一个静态的对象,不管以后用不用,都会占据内存,相应的在第一次调用的速度会很快,已经初始化完成了。

  懒汉式会延迟加载,第一次使用该单例的时候才会实例化该对象出来,第一次调用会初始化,速度上比较慢一点。

    1、同步锁

      在方法上调用上加上同步锁,线程安全了,但是每次都要同步,会影响性能

    2、双重检查

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

    3、静态内部类

      利用了classloader的机制来保证初始化INSTANCE时只有一个线程,所以线程也是安全的,同时没有性能的损耗。(推荐)

 

 

什么是线程安全:

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

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

 

 

单例模式的应用:

  饿汉式,使用双重检查

 

 1 package cn.zpoor.singletonBlog;
 2 public class Singleton {
 3     private String name;
 4     private static volatile Singleton instance = null;
 5 
 6     public String getName() {
 7         return name;
 8     }
 9 
10     public void setName(String name) {
11         this.name = name;
12     }
13     
14     private Singleton() {}
15     
16     public static Singleton getInstance() {
17         if (instance == null) {
18             synchronized (Singleton.class) {
19                 if (instance == null) {
20                     instance = new Singleton();
21                 }
22             }
23         }
24         return instance;
25     }
26     
27     public void info() {
28         System.out.println("name is" + name);
29     }
30 }

测试代码:

  

 1 package cn.zpoor.singletonBlog;
 2 
 3 public class TestSingleton {
 4     public static void main(String[] args) {
 5         Singleton s1 = Singleton.getInstance();
 6         Singleton s2 = Singleton.getInstance();
 7         
 8         s1.setName("WY");
 9         s2.setName("Zpoor");
10         
11         s1.info();
12         s2.info();
13         
14         if (s1 == s2) {
15             System.out.println("这是单例模式");
16         } else {
17             System.out.println("这是多例模式");
18         }
19     }
20 }
21 
22 
23 /*结果:
24       
25     name isZpoor
26     name isZpoor
27     这是单例模式
28  */

 

总结:

  一个类只有一个实例,还要提供全部访问的方法,注意懒汉饿汉的区别,线程安全等等。

 

posted @ 2017-11-22 23:44  薛定谔的猫。  阅读(231)  评论(0编辑  收藏  举报