文章目录
我们常使用单例模式来解决多线程或并发场景下变量数据的安全问题,避免脏数据的产生。
本文主要简单总结下Java多线程中使用单例模式思想的各种实现,并比较这些单例具体实现的优点和缺陷。
文章内容摘自:《Java多线程编程核心技术》 高洪岩 著
单例模式分析
立即加载(饿汉模式)
立即加载:需要使用类的时候,我们已经将对象创建完毕。
饿汉模式,指我们在调用方法前,实例已经被创建了,示例代码如下:
package test;
public class MyObject{
private static MyObject myObject = new MyObject();
private MyObject(){
}
public static MyObject getInstance(){
return myObject;
}
}
创建线程类MyThread.java代码如下:
import test.MyObject;
public class MyThread extends Thread{
@Override
public void run(){
System.out.println(MyObject.getInstance().hashCode());
}
}
创建一个运行主类Run.java:
import extthread.MyThread;
public class Run{
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}
执行一下,我们可以看到打印的hashCode(哈希码,对象实例的唯一标识,类似于我们每个人的身份证号码)是同一个值。以上场景便是立即加载的单例设计模式。
延迟加载(懒汉模式)
延迟加载:指在调用get()时候实例才被创建。
MyObject.java
package test;
public class MyObject{
private static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance(){
//延迟加载
if(myObject == null){
myObject = new MyObject();
}
return myObject;
}
}
虽然“延迟加载”实现了单例设计模式,但在多线程的环境下,上面“延迟加载”的代码完全是错误的,根本不能保持单例的状态。
懒汉模式保证单例的解决方案
1. synchronized关键字实现
-
锁getInstance()方法:运行效率低
-
锁类(Object.class):运行效率低
-
针对某部分重要的代码进行单独锁同步:还是存在一定的多线程安全隐患
-
使用DCL双检查锁机制
2. 静态内置类实现单例模式
DCL可以解决多线程单例的安全问题;用其他的办法也能达到同样的效果。
3. 序列化和反序列化实现单例
静态内置类可以解决线程安全问题。可如果遇到序列化对象时,使用默认的方式运行,得到的结果还是多例的。
可以用以下的readResolve()方法解决
4. 使用static代码块实现单例
package test;
public class MyObject{
private static MyObject instance = null;
private MyObject(){}
static{
instance = new MyObject();
}
public static MyObject getInstance(){
return instance;
}
}
5. 使用enum枚举实现单例
6. 完善使用enum实现单例模式
上面的示例将枚举类进行暴露,违反了“职责单一原则”,接下来可以进行完善
MyThread.java代码如下
package extthread;
public class MyThread extends Thread{
@Override
public void run(){
for(int i=0; i< 5 ; i++){
System.out.println(MyObject.getConnection().hashCode());
}
}
}