单例模式(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 }