单例设计模式

Posted 海豚汪洋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例设计模式相关的知识,希望对你有一定的参考价值。

单例模式(Singleton)

简单来说,单例模式就是创建一个类,仅产生一个实例供外部访问。

1.1 实现方案

方案一:

 1 /**
 2  * 单例模式:方案一
 3  */
 4 public class Singleton {
 5 
 6     // 将构造方法私有化,防止以new的方式创建对象并且实例化
 7     private Singleton() {  }
 8 
 9     // 定义一个引用自身对象的属性,该属性为静态常量
10     private static final Singleton instance = new Singleton();
11 
12     /**
13      * 静态方法,返回该类的实例
14      * @return
15      */
16     public static Singleton getInstance() {
17         return instance;
18     }
19 }

       该方法较为简单,而且获取的实例是静态常量,因此不存在线程安全问题,完全摒弃了synchronized造成的性能问题。然而,当该类被加载时,就会创建静态常量对象,并且该对象会一直占有内存,直到该类卸载,因此有些情况下会造成内存问题。

       方案二:

 1 /**
 2  * 单例模式:方案二
 3  */
 4 public class Singleton {
 5 
 6     // 将构造方法私有化,防止以new的方式创建实例
 7     private Singleton() {  }
 8 
 9     // 定义一个自身类型的静态变量
10     private static Singleton instance = null;
11 
12     /**
13      * 静态方法,返回该类的实例
14      * @return
15      */
16     public static Singleton getInstance() {
17         // 判断该实例是否存在
18         if (null == instance)
19             instance = new Singleton();
20         return instance;
21     }
22 }

       方案二仅仅是基于内存的节省对方案一的改造,但是如果在多线程环境下,有可能会产生多个实例对象,因此不是线程安全的。

       方案三:

 1 /**
 2 
 3  * 单例模式:方案三
 4 
 5  */
 6 
 7 public class Singleton {
 8 
 9     // 将构造方法私有化,防止以new的方式创建实例
10 
11     private Singleton() {  }
12 
13 
14     // 定义一个自身类型的静态变量
15 
16     private static Singleton instance = null;
17 
18     /**
19 
20      * 静态方法,返回该类的实例
21 
22      * 方法添加同步锁,防止多线程访问产生多个实例
23 
24      * @return
25 
26      */
27 
28     public synchronized static Singleton getInstance() {
29 
30         // 判断该实例是否存在
31 
32         if (null == instance)
33 
34             instance = new Singleton();
35 
36         return instance;
37     }
38 }

       方案三在方案二的基础上为静态方法添加同步锁,以达到线程安全的要求。但是同步方法被频繁调用时,当然会存在效率问题。

       方案四:

 1 /**
 2 
 3  * 单例模式:方案四
 4 
 5  */
 6 
 7 public class Singleton {
 8     // 将构造方法私有化,防止以new的方式创建实例
 9 
10     private Singleton() {  }
11 
12     // 定义一个自身类型的静态变量
13 
14     private static Singleton instance = null;
15 
16     /**
17 
18      * 静态方法,返回该类的实例
19 
20      * 方法内部添加同步块,不用每次调用方法都要获取同步锁
21 
22      * @return
23 
24      */
25 
26     public static Singleton getInstance() {
27 
28         // 判断该实例是否存在
29 
30         if (null == instance) {
31 
32             // 如果不存在,才获取同步锁,如果存在则直接返回对象
33 
34             synchronized(Singleton.class) {
35 
36                 if (null == instance) {
37 
38                     instance = new Singleton();
39 
40                 }
41 
42             }
43 
44         }
45         return instance;
46     }
47 }

       方案四改进了方案三,保证只有在必要的情况下,即当对象没有创建的时候才获取同步锁,如果对象已经存在,则直接返回即可,实现了高效并且线程安全。

       方案四需要注意同步块中又进行了一次判断,原因是,如果当代码执行到第一次判断时,有可能另一个线程刚好创建了一个实例,因此,获取锁之后还要判断一次。

1.2 应用举例

例如,在使用Jedis的时候,由于多线程下频繁使用,因此需要使用连接池来管理多个Jedis连接,而为了保证连接池只有一个,则需要采用单例模式,如果不用Spring来管理连接池,则需要使用单例模式:

 1 /**
 2 
 3  * Redis连接池工具
 4 
 5  */
 6 
 7 public class JedisPoolUtil {
 8 
 9     private static volatile JedisPool jedisPool = null;
10 
11     // 提供一个私有构造函数保证单例
12 
13     private JedisPoolUtil() { }
14     /**
15 
16      * 获取一个<tt>JedisPool</tt>
17 
18      * @return jedisPool 一个单例的{JedisPool}
19 
20      */
21 
22     public static JedisPool getJedisPoolInstance() {
23         if (null == jedisPool) {
24             synchronized (JedisPool.class) {
25                 if (null == jedisPool) {
26                     JedisPoolConfig poolConfig = new JedisPoolConfig();
27                     // poo2已经更改maxActive为maxTotal
28                     poolConfig.setMaxTotal(32);
29                     // pool2已经更改为maxWaitMillis
30                     poolConfig.setMaxWaitMillis(100 * 1000);
31                     jedisPool = new JedisPool(poolConfig, "0.0.0.0", 6379);
32                 } // if end
33             } // synchronized end
34         } // if end
35         return jedisPool;
36     }
37     public static void release(JedisPool jedisPool) {
38         Jedis jedis = null;
39         try {
40             jedis = jedisPool.getResource();
41         } finally {
42             if (null != jedis)  jedis.close();
43         }
44     }
45 }

 

以上是关于单例设计模式的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

性能比较好的单例写法

片段作为 Android 中的单例

单例片段或保存网页视图状态

从 Viewpager2 片段访问父片段函数

你熟悉的设计模式都有哪些?写出单例模式的实现代码