破坏单例的方式
- 反射
- 序列化与反序列化
- 克隆
代码实现
public class DCSingleton implements Serializable, Cloneable {
/**
* 这里加入volatile修饰,利用的volatile的有序性
*/
private static volatile DCSingleton singleton;
/**
* 禁止外部代码用new构造实例
*/
private DCSingleton() {
}
public static DCSingleton getInstance() {
/**
* 第一次检查
*/
if (singleton == null) {
synchronized (DCSingleton.class) {
/**
* 第二次检查,可以思考一下为什么需要进行第二次检查
*/
if (singleton == null) {
/**
* volatile关键字作用所在
* = new 这两个动作并不是原子操作,为线程不安全,在《java并发编程的艺术》此书上有详细介绍
*/
singleton = new DCSingleton();
}
}
}
return singleton;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
@Slf4j
public class TestSingleton {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, IOException, CloneNotSupportedException, InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("实例={}", DCSingleton.getInstance());
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
DCSingleton singleton = DCSingleton.getInstance();
Class clazz = Class.forName(singleton.getClass().getName());
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
log.info("反射构造的实例={}", constructor.newInstance());
File file = new File("serial.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
objectOutputStream.writeObject(singleton);
objectOutputStream.flush();
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
DCSingleton deSerialSingleton = (DCSingleton) objectInputStream.readObject();
log.info("反序列化后的实例={}",deSerialSingleton);
DCSingleton cloneSingleton = (DCSingleton) singleton.clone();
log.info("克隆后得到的实例={}",cloneSingleton);
}
}
执行结果
10:21:13.355 [Thread-4] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-2] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-1] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-0] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.355 [Thread-3] INFO com.kk.singleton.TestSingleton - 实例=com.kk.singleton.doubleCheck.DCSingleton@3868223f
10:21:13.363 [main] INFO com.kk.singleton.TestSingleton - 反射构造的实例=com.kk.singleton.doubleCheck.DCSingleton@1a6c5a9e
10:21:13.388 [main] INFO com.kk.singleton.TestSingleton - 反序列化后的实例=com.kk.singleton.doubleCheck.DCSingleton@49c2faae
10:21:13.388 [main] INFO com.kk.singleton.TestSingleton - 克隆后得到的实例=com.kk.singleton.doubleCheck.DCSingleton@20ad9418
怎么保护单例不被破坏
- 反射的解决方式
- 反序列化的解决方式
反序列化生成实例,并不会调用当前类的构造方法,并不能通过处理反射的方式来解决。
jdk提供了一种方式,让单例类增加一个成员方法即可解决。方法如下
private Object readResolve() throws ObjectStreamException{
return singleton;
}
具体原因可以去查看Object输入输出流的源码。
反序列化虽然不会调用被反序列化的类的构造函数,但是会调用其超类的构造函数,我们也可以根据这个思路去解决反序列破坏单例的问题,但这里不做推荐。
- 克隆的解决方式
可以仿照上面的处理方式