懒汉模式与饿汉模式
懒汉模式就是懒加载,用到的时候去加载,存在线程安全问题,需要手动地加锁控制。它的优点是类加载的速度比较快,按需加载,节省资源。
饿汉模式就是在类加载的时候会创建出实例。它天生就不存在线程安全问题。但是类加载的速度会变慢且耗费资源。
懒汉模式-单重检查
示例代码如下:
public class LazySingleton {
private static LazySingleton singletoninstance = null;
private Object data = new Object();
//私有化构造方法
private LazySingleton(){
}
//加锁访问
public static synchronized LazySingleton getInstance(){
if(singletoninstance == null){
singletoninstance = new LazySingleton();
}
return singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
测试代码如下:
public class TestThread extends Thread {
@Override
public void run() {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance.getData());
}
}
public static void main(String[] args) {
for(int i =0;i < 10;i++){
TestThread t = new TestThread();
t.start();
}
}
}
运行结果如下:
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
java.lang.Object@306d3b64
打印出同一个object对象,表明是从同一个LazySingleton对象中获取的数据。
但是上述代码存在一个显著的问题:多个线程同时访问getInstance()方法都是排队式的,即使该instance已经被创建的情况下。然而,如果该instance已经被创建,是可以支持并发访问的。需要对锁的控制细粒度化。
懒汉模式-双重检查
public class LazySingleton {
//声明为volatile变量
private static volatile LazySingleton singletoninstance = null;
private Object data = new Object();
private LazySingleton(){
}
public static synchronized LazySingleton getInstance(){
if(singletoninstance == null){
synchronized (LazySingleton.class) {
//这个第二重的的检查是必要的
if(singletoninstance == null)
singletoninstance = new LazySingleton();
}
}
return singletoninstance;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
第二重检查是为了防止:
线程A发现instance未被创建,于是申请锁,进入临界区创建instance;于此同时另一个线程也发现instance未被创建,于是也要申请锁去创建instance,问题就这样发生了。而且,这个instance变量要被声明为volatile,也就是其中一个线程对它就行修改之后(也就是实例化),这一修改立马对其他线程可见,避免了无谓的等待。










