单例模式,在程序运行期间只有一个实例存在。
简单饿汉式单例模式实现代码如下:
package com.demo; public class Singleton { private Singleton() { } public static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } }
空间换时间。
package com.demo; public class Singleton { private Singleton() { } public static Singleton instance; public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
时间换空间。
简单的单例模式在多线程中容易出现问题,获取到的instance并非同一个对象。
同步方法实现单例模式:
package com.demo; public class Singleton { private Singleton() { } public static Singleton instance; public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }同步方法实现单例模式,效率低下,每一线程进入,都将阻塞其它线程的访问。
双重检查加锁实现单例模式:
package com.demo; public class Singleton { private Singleton() { } public static Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if(instance == null){ instance = new Singleton(); } } } return instance; } }双重检查加锁(Double Checked Locking)很巧妙实现了单例模式,步骤为:
1、检查变量是否初始化(不加锁),如果已经初始化,立即返回变量。
2、获得锁。
3、再次检查变量是否初始化,如果变量已经被之前的某个线程初始化,立即返回此变量。
4、否则的话初始化变量并返回。
遗憾的是,传统的双重检查在java中并不可靠,双重检查锁定的理论是完美的,不幸的是,现实完全不同,java并不能保证它在单处理器或者多处理器计算机上顺利运行。
java平台的内存模型允许“无序写入”,instance的赋值和初始化的顺序是不确定的,在new 一个对象之前instance的值就已经不为null了,所以如果此时有线程执行到if(instance==null)的条件判断的时候就已经返回了一个不为null却未被初始化的instance。
解决方案:1、可以采用“饿汉式”解决,多线程下jvm可以保证类中静态内容只被初始化一次。
2、非要用双重加锁的话,变量前加volatile关键字(jdk5+),告知java虚拟机对加volatile的变量的读写不做优化排序。
package com.demo; public class Singleton { private Singleton() { } public static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if(instance == null){ instance = new Singleton(); } } } return instance; } }
package com.demo; import java.util.concurrent.locks.ReentrantLock; public class Singleton { /** * 私有构造 */ private Singleton() { } /** * 互斥锁 */ private static final ReentrantLock lock = new ReentrantLock(); public static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { /** * 锁定访问线程,instance需要被修饰成volatile */ lock.lock(); try { if (instance == null) { instance = new Singleton(); } } finally { /** * 解锁 */ lock.unlock(); } } return instance; } }
高级一点的采用读写锁方式:
package com.demo; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 单例模式注册类,此类本身使用饿汉式单例模式,用来管理所有的单例对象 * * @author kk * */ public class SingletonRegister { /** * 私有构造 */ private SingletonRegister() { } /** * 本身单例的引用 */ private static final SingletonRegister instance = new SingletonRegister(); /** * 维护注册信息的map */ private static Map<String, Object> map = new HashMap<String, Object>(); /** * 多线程的可重入的读写锁 */ private static final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); /** * 可读锁,可以多个只读锁并存,锁定时可写锁不可用 */ private static final Lock rLock = rwLock.readLock(); /** * 可写锁,只能有一个,锁定时其它锁不可用 */ private static final Lock wLock = rwLock.writeLock(); public static Object getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException { Object instance = null; // 锁定读取锁 rLock.lock(); instance = map.get(className); try { if (instance != null) { return instance; } } finally { // 释放读取锁 rLock.unlock(); } // 锁定写入的锁 wLock.lock(); try { instance = map.get(className); if (instance != null) { return instance; } // 使用反射方式实例化单例对象 instance = Class.forName(className).newInstance(); // 放入map中存储 map.put(className, instance); } finally { wLock.unlock(); } return instance; } }
package com.demo; public class Singleton { /** * 私有构造 */ private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } /** * 内部类 * @author kk * */ public static class SingletonHolder { private static final Singleton instance = new Singleton(); } }内部类在使用到的时候,才开始加载初始化,由java虚拟机保证了线程安全以及延时加载。