首先来分析如下单例模式所存在的问题
饿汉式单例模式
测试代码:
public class synDemo01 {
static Jvm j1=null;
static Jvm j2=null;
public static void main(String[] args) {
Thread t1=new Thread("thread1"){
public void run() {
j1=Jvm.getJvm(600);
System.out.println(j1);
};
};
Thread t2=new Thread("thread2"){
public void run() {
j2=Jvm.getJvm(500);
System.out.println(j2);
};
};
t1.start();
t2.start();
}
}
单例JVM
class Jvm{
private static Jvm instance=null;
private Jvm()
{
}
public static Jvm getJvm(int time)
{
if(instance==null)
{
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new Jvm();
}
return instance;
}
}
测试结果
com.txr.thread.Jvm@263c8db9
com.txr.thread.Jvm@517c804b
我们发现在多线程之下,竟然实例化了俩个对象
对代码加以改进
单例JVM
class Jvm{
private static Jvm instance=null;
private Jvm()
{
}
public static Jvm getJvm(int time)
{
//效率不高,不管任何时候都需要等待,存在对象也需要等待
synchronized (Jvm.class) {
if(instance==null)
{
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new Jvm();
}
}
return instance;
}
}
测试结果
com.txr.thread.Jvm@517c804b
com.txr.thread.Jvm@517c804b
发现多线程下的单例是实现了,但是效率却不高,进行如下改进
class Jvm{
private static Jvm instance=null;
private Jvm()
{
}
public static Jvm getJvm(int time)
{
//提高效率相当于只锁定了判断
if(instance==null)
{
synchronized (Jvm.class) {//锁定字节码信息
if(instance==null)
{
try {
Thread.sleep(time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
instance=new Jvm();
}
}
}
return instance;
}
}
测试结果
com.txr.thread.Jvm@517c804b
com.txr.thread.Jvm@517c804b
发现效率提高(从等待时间来看)因为在第一个多线程下的单例模式中,每一个对象进行判断都需要等待,而第二个多线程的单例模式中,当第一个实例化后第二个则无需等待了,这样能大大提高效率
饿汉式的多线程下的单例模式
//饿汉式
class JVM2{
private static JVM2 instance=new JVM2();
private JVM2()
{
}
public static JVM2 getJVM3(int time)
{
return instance;
}
}
很显然在饿汉式的单例模式中不会在多线程的情况下出现问题,因为在类被加载时就已经创建好了实例只需要返回
那么如何进行优化呢?
//饿汉式
class JVM3{
//类在使用时才会加载,延缓了加载时间
private static class JVMholder{
private static JVM3 instance=new JVM3();
}
private JVM3()
{
}
public static JVM3 getJVM3(int time)
{
return JVMholder.instance;
}
}
将实例化的对象放入内部类中,由类的加载性质我们可以知道这个实例只有在需要的时候才会被创建,这样做的好处就是延缓了加载时间,只有在用到的时候才会被加载.