内容都是从Tom老师的书中摘抄,记录下来方便自己学习。
单例模式(singleton pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例是创建型模式。
一、饿汉式单例模式是在类加载时就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没有出现前就实例化了,不可能存在访问安全问题。饿汉式单例模式适用于单例对象较少的情况。
public class HungrySingleton {
private static final HungrySingleton hungrySingleton=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
另一种写法
public class HungrySingleton {
private static final HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
}
二、懒汉式单例模式是指被外部类调用的时候内部类才会加载。
/*
*此种方式存在线程安全问题
*/
public class LazySimpleSingleton {
private LazySimpleSingleton(){}
private static LazySimpleSingleton lazySimpleSingleton=null;
public static LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
public class ExectorThread implements Runnable {
public void run() {
LazySimpleSingleton simpleSingleton = LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + simpleSingleton);
}
}
public class LazySimpleSignletonTest {
public static void main(String[] args) {
Thread t1 = new Thread(new ExectorThread());
Thread t2 = new Thread(new ExectorThread());
t1.start();
t2.start();
System.out.println("end");
}
}
加入断点,右键断点,选中Thread模式
不断切换线程,观察单例对象的内存状态,发现单例对象被实例化两次,后面的实例覆盖了前面的实例,我们看到的是一个假象,认为线程是安全的
/*
*加入synchroized 关键字
*/
public class LazySimpleSingleton {
private LazySimpleSingleton(){}
private static LazySimpleSingleton lazySimpleSingleton=null;
public synchronized static LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞,直到第一个线程执行完
兼顾线程安全又能提升程序性能的写法
/*
*
*/
public class LazySimpleSingleton {
private LazySimpleSingleton(){}
private volatile static LazySimpleSingleton lazySimpleSingleton=null;
public static LazySimpleSingleton getInstance(){
if (lazySimpleSingleton == null) {
synchronized (LazySimpleSingleton.class){
if (lazySimpleSingleton == null) {
lazySimpleSingleton=new LazySimpleSingleton();
}
}
}
return lazySimpleSingleton;
}
}
通过调试,当一个线程调用getInstance()方法时,另一个线程在调用getInstance()方法,线程的状态变为Monitor,出现阻塞。此时,阻塞并不是基于整个LazySimpleSingleton类,而实在getInstance()方法内部,只要逻辑不太复杂,对于调用者而言感知不到。(此处没有测试性能差距,留个位置,测试后补充)
不使用synchronized关键字的写法
/*
* 采用静态内部类的写法,这种方式兼顾了饿汉式单例模式的内存浪费问题和synchronized的性能问题,内部类一定是在方法掉用之前初始化,巧妙避免了线程安全问题。
*/
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){}
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY_INNER_CLASS_SINGLETON;
}
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY_INNER_CLASS_SINGLETON=new LazyInnerClassSingleton();
}
}
/*
* 反射破坏单例 ,代码是随意写的,主要为了测试用
*/
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
Class<?> clazz=LazyInnerClassSingleton.class;
Constructor<?> constructor= null;
try {
constructor = clazz.getDeclaredConstructor(null);
constructor.setAccessible(true);
try {
Object o1=constructor.newInstance();
Object o2=constructor.newInstance();
System.out.println(o1==o2);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
/*
* 序列化破坏单例
*/
public class Seriablesingleton implements Serializable {
private final static Seriablesingleton INSTANCE=new Seriablesingleton();
private Seriablesingleton(){}
public static Seriablesingleton getInstance(){
return INSTANCE;
}
// private Object readResolve(){
// return INSTANCE;
// }
}
/*
* 将上述代码注释部分取消注释即可
* 虽然增加readResolve()方法返回实例解决了单例模式被破坏问题,但实际上实例化了两次,只是新创建的实例没有被返回。
* 如果创建对象的动作发生频率加快,就意味着内存分配开销也会随之增大
*/
public class SeriablesingletonTest {
public static void main(String[] args) {
Seriablesingleton s1=null;
Seriablesingleton s2=Seriablesingleton.getInstance();
FileOutputStream fos=null;
try {
fos=new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.close();
FileInputStream fis=new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois=new ObjectInputStream(fis);
s1= (Seriablesingleton) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}