java单例模式代码

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

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


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


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







你可能感兴趣的:(java,设计模式,多线程,编程,单例模式)