创建型模式
确保一个类在任何情况下都绝对只有一个实例,并且提供一个全局访问点。
在类加载的时候就立即初始化,并且创建单例对象。
绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题
//标准代码
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
//静态代码块
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingleton;
static {
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton() {
}
public static HungryStaticSingleton getInstance() {
return hungrySingleton;
}
}
适用:
单例对象较少的情况,可以保证线程安全,执行效率高
缺点:
所有对象类加载的时候就实例化,如果系统中有大批量的单例对象存在,那么系统初始化导致大量的内存浪费
单例对象要在被使用的时候才会初始化
public class LazySimpleSingleton {
private LazySimpleSingleton() {}
private static LazySimpleSingleton lazy = null;
public static LazySimpleSingleton getInstance() {
if(lazy == null){
lazy = new LazySimpleSingleton();
}
return lazy;
}
}
导致一个新的问题,如果在多线程环境下,会出现线程安全问题
public class ExectorThread implements Runnable {
@Override
public void run() {
LazySimpleSingleton instance = LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}
}
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(new ExectorThread());
Thread thread1 = new Thread(new ExectorThread());
thread.start();
thread1.start();
System.out.println("END");
}
}
DEBUG多线程环境下,可以看到LazySimpleSingleton被实例化了两次,可能会出现打印结果一致的情况,实际也是后面执行的线程覆盖了前者的结果。
public class LazySingleton {
private LazySingleton() {}
private static LazySingleton lazy = null;
public synchronized static LazySingleton getInstance() {
if(lazy == null){
lazy = new LazySingleton();
}
return lazy;
}
}
加上synchronized关键字加锁,如果在线程数量较多的情况下,如果CPU分配压力上升,会导致大批线程阻塞,导致程序性能下降。
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazy = null;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance() {
if (lazy == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazy == null) {
lazy = new LazyDoubleCheckSingleton();
}
}
}
return lazy;
}
}
第一个线程调用getInstance()时,第二个线程也可以调用。当一个线程执行 synchronized 时会上锁,第二个线程会变成MONITOR状态,出现阻塞,阻塞并不是基于整个LazyDoubleCheckSingleton类的上锁,而是在getInstance()内部的阻塞,只要逻辑不是很负责,调用者感受不到阻塞的时间差。
将每一个实例都登记到某一个地方,使用唯一的标识获取实例。
public enum EnumSingleton {
INSTANCE;
private Object data;
public void setData(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
public static void main(String[] args) {
try {
EnumSingleton instance = EnumSingleton.getInstance();
instance.setData(new Object());
FileOutputStream fos = new FileOutputStream("EnumSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("EnumSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
EnumSingleton enumSingleton = null;
enumSingleton = (EnumSingleton) ois.readObject();
ois.close();
System.out.println(instance.getData());
System.out.println(enumSingleton.getData());
System.out.println(enumSingleton.getData() == instance.getData());
} catch (Exception e) {
e.printStackTrace();
}
}
枚举式单例模式在静态代码块中就给INSTANCE进行赋值,是饿汉式单例模式的体现。
看源代码:
再看反射是否能破坏单例:
try{
Class enumSingletonClass = EnumSingleton.class;
Constructor c = enumSingletonClass.getDeclaredConstructor();
c.newInstance();
}catch (Exception e){
e.printStackTrace();
}
没有找到无参的构造方法
修改代码
不能用发射来创建枚举类型
在源码中,newInstance()做了强制性的判断
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map ioc = new ConcurrentHashMap();
public static Object getBean(String className) {
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(className);
}
}
}
}
适用于需要大量创建单例对象的场景,便于管理。但是是非线程安全的。
public class ThreadLocalSingleton {
private static final ThreadLocal threadLocalInstance =
new ThreadLocal() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
public class test {
public static void main(String[] args) {
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
Thread thread = new Thread(new ExectorThread());
Thread thread1 = new Thread(new ExectorThread());
thread.start();
thread1.start();
}
}
主线程无论调用多少次,获取到的实例是同一个,都在连个子线程中分别获取到了不同的实例。
ThreadLocal将所有的对象全部放在ThreadLocalMap中,为每个线程都提供一个对象,实际上以空间换时间来实现线程隔离。
try {
Class> clazz = LazyInnerSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor(null);
//强制访问
constructor.setAccessible(true);
//强制初始化
Object o = constructor.newInstance();
Object o1 = constructor.newInstance();
System.out.println(o == o1);
} catch (Exception e) {
e.printStackTrace();
}
强制初始化后,出现两个不同的实例,优化重复创建抛出异常
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton() {
//加个判断
if(LazyHolder.LAZY != null){
throw new RuntimeException("不允许创建多个实例");
}
}
//默认不加载
private static class LazyHolder {
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
//static是为了使单例的空间共享,保证方法不会被重写重载
public static final LazyInnerClassSingleton getInstance() {
//返回结果前,一定先加载内部类
return LazyHolder.LAZY;
}
}
单例创建好后,有时候需要将对象序列化再写入磁盘,读取的时候再进行反序列化,转化为内存对象。反序列化后的对象会重新分配内存。
public class SerializableSingleton implements Serializable {
private SerializableSingleton() {}
private final static SerializableSingleton INSTANCE = new SerializableSingleton();
public static SerializableSingleton getInstance() {
return INSTANCE;
}
}
SerializableSingleton s1 = null;
SerializableSingleton s2 = SerializableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SerializableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
System.out.println(e);
}
反序列化后的对象和手动创建的对象是不同的
优化代码:
public class SerializableSingleton implements Serializable {
private SerializableSingleton() {}
private final static SerializableSingleton INSTANCE = new SerializableSingleton();
public static SerializableSingleton getInstance() {
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}
原理:
虽然readResolve()方法返回实例解决了单例模式被破坏的问题,但实际上是实例化了两次,只不过新创建的对象没有被返回。如果创建对象的动作发生频繁加快,就以为这内存分配开销也会随之增大。