单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。属于创建型模式。在 J2EE 标准中,ServletContext、 ServletContextConfig 等;在 Spring 框架应用中 ApplicationContext;数据库的连接 池也都是单例形式。
一.饿汉式单例
饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线 程还没出现以前就是实例化了,不可能存在访问安全问题。 Spring 中 IOC 容器 ApplicationContext 本身就是典型的饿汉式单例。
优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。
缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能占着茅 坑不拉屎。
package hungry;
import java.io.Serializable;
/**
* 饿汉模式:类加载的时候直接创建实例,需要的时候直接返回实例对象
*/
public class HungrySingleton implements Serializable {
//1.恶汉第一种方式:静态属性
private static HungrySingleton hungrySingleton = new HungrySingleton();//类加载的时候执行,有且只执行一次
//2.恶汉第二种:静态代码块
/* static{
hungrySingleton = new HungrySingleton();
System.out.println("HungrySingleton静态代码块执行hungrySingleton="+hungrySingleton);
}*/
private HungrySingleton(){
System.out.println("HungrySingleton私有无参构造执行.......");
};
public static HungrySingleton getInstance(){
return hungrySingleton;
}
/**
* 阻止,反序列化时破坏单例模式
* @return
*/
private Object readResolve(){
return hungrySingleton;
}
}
二.懒汉式单例
懒汉式单例的特点是:被外部类调用的时候内部类才会加载,存在线程安全问题。
package lazy;
import java.io.Serializable;
/**
* 懒汉单例:
* 需要的时候才创建实例
*/
public class LazySingleton{
private static LazySingleton lazySingleton = null;
private LazySingleton(){};
//非线程安全
public static LazySingleton getInstance(){
if (null == lazySingleton){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
//线程安全:方法加锁
public synchronized static LazySingleton getInstance1(){
if (null == lazySingleton){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
//线程安全:代码块加锁,双重校验
public static LazySingleton getInstance2(){
if (null == lazySingleton){
synchronized (LazySingleton.class){
if (null == lazySingleton){
lazySingleton = new LazySingleton();
//分配内存
//初始化对象
//设置lazySingleton指向内存地址
}
}
}
return lazySingleton;
}
}
饿汉式内部类
package lazy;
/**
* 饿汉式内部类:
* 既兼顾了饿汉式的内存浪费,也兼顾了synchronized的安全与性能问题。
*/
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){
//防止反射破坏单例模式
if (null != LazyHolder.LAZY){
throw new RuntimeException("LazyInnerClassSingleton不允许创建多个实例");
}
};
public static LazyInnerClassSingleton getIntance(){
return LazyHolder.LAZY;
}
/**
* 内部类默认不加载,只有在使用的时候才加载
*/
private static class LazyHolder{
private static LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
多线程测试是否线程安全
package lazy;
public class LazyExecutor implements Runnable{
public void run() {
// LazySingleton instance = LazySingleton.getInstance2();
LazyInnerClassSingleton instance = LazyInnerClassSingleton.getIntance();
long time = System.currentTimeMillis();
System.out.println("[time"+time+"]"+Thread.currentThread().getName()+":"+instance);
}
}
package lazy;
public class LazySingletonTest {
public static void main(String[] args) {
LazyExecutor lazyExecutor = new LazyExecutor();
for (int i = 0; i <10 ; i++) {
new Thread(lazyExecutor).start();
}
}
}
破坏单例模式的两种方法
package lazy;
import hungry.HungrySingleton;
import java.io.*;
import java.lang.reflect.Constructor;
public class BrokeLazyInnerClassSingleton {
public static void main(String[] args) {
//brokeByReflect();
brokeBySerializable();
}
/**
* 通过反射强行访问私有构造方法,破坏单例模式
*/
private static void brokeByReflect(){
Class<?> clzz = LazyInnerClassSingleton.class;
Constructor<?> cs = null;
Object obj1 = null;
try {
cs = clzz.getDeclaredConstructor(null);
cs.setAccessible(true);
obj1 = cs.newInstance();
System.out.println(obj1);
} catch (Exception e) {
e.printStackTrace();
}
LazyInnerClassSingleton obj2 = LazyInnerClassSingleton.getIntance();
System.out.println(obj2);
}
/**
* 通过反序列化破坏单例模式
**/
private static void brokeBySerializable(){
HungrySingleton obj1 = HungrySingleton.getInstance();
System.out.println(obj1);
HungrySingleton obj2 = null;
try {
FileOutputStream os = new FileOutputStream("HungrySingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(obj1);
oos.flush();
oos.close();
os.close();
FileInputStream fis = new FileInputStream("HungrySingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
//重写readResolve()
obj2 = (HungrySingleton)ois.readObject();
ois.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(obj2);
System.out.println(obj1==obj2);
}
}
三.注册式单例
注册式单例又称为登记式单例,就是将每一个实例都登记到某一个地方,使用唯一的标 识获取实例。注册式单例有两种写法:一种为容器缓存,一种为枚举登记。
枚举式单例
天生的线程安全。枚举式单例在静态代码块中就给 INSTANCE 进行了赋值,是饿汉式单例的实现。
不能 用反射来创建枚举类型。进入 Constructor 的 newInstance()方法:在 newInstance()方法中做了强制性的判断,如果修饰符是 Modifier.ENUM 枚举类型, 直接抛出异常。
package register;
/**
* 枚举式单例
* 不能被反射和反序列化破坏单例模式
*/
public enum EnumSingle {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingle getInstance(){
return INSTANCE;
}
}
容器式单例
package register;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContainerSingleton {
private ContainerSingleton(){};
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
/**
* 存在线程问题,所以要使用synchronized加锁
* @param className
* @return
*/
public static Object getBean(String className){
synchronized (ioc) {
if (!ioc.containsKey(className)) {
try {
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
ioc.put(className, obj);
return obj;
} catch (Exception e) {
e.printStackTrace();
}
}
return ioc.get(className);
}
}
}
四.ThreadLocal 本地线程变量实现单例模式
ThreadLocal 不能保证其 创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全。
package threadlocal;
/**
* 天生线程内安全
* 保证单线程内单例模式的实现
*/
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue(){
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){};
public static ThreadLocalSingleton getInstance(){
return threadLocal.get();
}
}
多线程测试
package threadlocal;
public class ThreadLocalTest {
public static void main(String[] args) {
for (int i = 0; i <20 ; i++) {
new Thread() {
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+":start run.......");
ThreadLocalSingleton bean1 = ThreadLocalSingleton.getInstance();
ThreadLocalSingleton bean2 = ThreadLocalSingleton.getInstance();
ThreadLocalSingleton bean3 = ThreadLocalSingleton.getInstance();
ThreadLocalSingleton bean4 = ThreadLocalSingleton.getInstance();
ThreadLocalSingleton bean5 = ThreadLocalSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+":"+bean1);
System.out.println(Thread.currentThread().getName()+":"+bean2);
System.out.println(Thread.currentThread().getName()+":"+bean3);
System.out.println(Thread.currentThread().getName()+":"+bean4);
System.out.println(Thread.currentThread().getName()+":"+bean5);
System.out.println(Thread.currentThread().getName()+":end run.......");
}
}.start();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.fillInStackTrace();
}
}
}
}
测试结果可以看出,在每个线程内可实现单例模式。