【探索】单例模式与多线程

单例模式结合多线程技术

1.饿汉模式/“立即加载”

立即加载就是使用类的时候已经将对象创建完毕,常见的实现方法是直接new 实例化。

1.1 立即加载型单例模式

创建MyObject.java:

public class MyObject {
    private static MyObject myObject = new MyObject();
    private MyObject(){

    }

    public static MyObject getInstance(){
        return myObject;
    }
}

创建MyThread.java:

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(MyObject.getInstance().hashCode());
    }
}

创建测试类Run.java:

public class Run {
    public static void main(String[] args) {
            MyThread a1 = new MyThread();
            MyThread a2 = new MyThread();
            MyThread a3 = new MyThread();
            a1.start();
            a2.start();
            a3.start();
    }
}

最后的运行结果如下,可见是同一个对象,即实现了立即加载型单例模式。

1097280301
1097280301
1097280301

2.延迟加载/“懒汉模式”

延迟加载就是在调用get()方法时实例才被创建,常见的实现方法就是在get()方法中进行new实例化,延迟加载也被称作"懒汉模式"
修改MyObject类如下:

public class MyObject {
    private static MyObject myObject;
    private MyObject(){

    }
    public static MyObject getInstance(){
        //延迟加载
        if(myObject == null){
            myObject = new MyObject();
        }
        return myObject;
    }
}

最后的运行结果如下,能够看到多线程环境中并不是同一个单例

980142845
1553816201
418194775
  • 上面的延迟加载/"饿汉模式"在多线程的环境中不能实现单例,解决方案如下:
    修改MyObject类如下:
public class MyObject {
    private static MyObject myObject;
    private MyObject(){
    }
    synchronized public static MyObject getInstance(){
        //延迟加载
        if(myObject == null) {
            myObject = new MyObject();
        }
        return myObject;
    }
}
1553816201
1553816201
1553816201

最后的运行结果虽然实现了是取的同一个实例,但是效率却非常的低,尝试同步代码块和直接同步方法是一样的,效率也提高不了多少,最后采用DCL双检查锁机制

DCL双检查锁机制

修改MyObject类如下:

public class MyObject {
    private static MyObject myObject;
    private MyObject(){
    }
     public static MyObject getInstance(){
        //延迟加载
        if(myObject == null) {
            synchronized(MyObject.class){
                if(myObject == null){
                    myObject = new MyObject();
                }
            }
        }
        return  myObject;
    }
}

最后的运行结果仍然是单例的,且提升了效率

1230761675
1230761675
1230761675

使用静态内置类实现单例模式

DCL可以解决多线程单例模式的非线程安全问题,当然其他的办法也能达到同样的效果
修改MyObject类如下:

public class MyObject {
    private MyObject(){
    }
     public static MyObject getInstance(){

        return  MyObjectHandler.myObject;
    }
    private static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }

}

最后的运行结果如下:

964250181
964250181
964250181

序列化与反序列化对象的单例模式实现

静态内置类可以达到线程安全问题,但如果遇到序列化对象时,使用默认的方式运行得到的结果还是多例的,解决办法就是在反序列化中使用readResolve()方法

public class MyObject implements Serializable {
    private MyObject(){
    }
     public static MyObject getInstance(){
        return  MyObjectHandler.myObject;
    }
    private static class MyObjectHandler{
        private static MyObject myObject = new MyObject();
    }

    protected Object readResolve(){
        return MyObjectHandler.myObject;
    }
}

创建测试类:

public class Run {
    public static void main(String[] args) {
        try {
            MyObject myObject = MyObject.getInstance();
            FileOutputStream fosRef = new FileOutputStream(new File("MyObject.txt"));
            ObjectOutputStream fo = new ObjectOutputStream(fosRef);
            fo.writeObject(myObject);
            fo.close();
            fosRef.close();
            System.out.println(myObject.hashCode());
            FileInputStream flsRef = new FileInputStream("MyObject.txt");
            ObjectInputStream ioRef = new ObjectInputStream(flsRef);
            MyObject m  = (MyObject)ioRef.readObject();
            ioRef.close();
            flsRef.close();
            System.out.println(m.hashCode());
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

最后的运行结果显示两者是单例的。

325040804
325040804

使用static代码块来实现单例模式(静态代码块中的代码在使用类的时候就已经执行了)

修改MyObject类如下:

public class MyObject {
    private static MyObject instan = null;
    static {
        instan = new MyObject();
    }
    private MyObject(){
    }
    public static MyObject getInstance(){
        return  instan;
    }
}

创建Run.java类:

public class Run {
    public static void main(String[] args) {
        MyThread a1 = new MyThread();
        MyThread a2 = new MyThread();
        MyThread a3 = new MyThread();
        a1.start();
        a2.start();
        a3.start();
    }
}

最后的运行结果如下:

2049367639
2049367639
2049367639

使用Enum枚举数据类型实现单例模式

枚举Enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用。即把MyObject类定义为枚举类

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章