java单例模式代码

来源:互联网 发布:2017年淘宝开店流程 编辑:程序博客网 时间:2024/05/13 20:32

单例模式,在程序运行期间只有一个实例存在。

简单饿汉式单例模式实现代码如下:


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虚拟机保证了线程安全以及延时加载。


总结:多线程总使用单例模式的建议是不要使用任何双重检查模式,优先考虑饿汉式以及内部类的方式实现单例模式即可,越简单越好;







0 0
原创粉丝点击