单例模式与多线程(二)

在单例模式与多线程(一)中,介绍了四种在多线程环境下实现的单例模式:饿汉模式、懒汉模式、静态内部类实现方式和static代码块实现方式。下面介绍使用enum枚举数据类型实现单例和使用序列化与反序列化实现单例。
下面先介绍使用enum枚举数据类型实现单例:
枚举enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,因此可以应用这个特性实现单例设计模式。
具体示例如下:

package com.javapatterns.singleton;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 使用enum枚举数据类型实现单例
 */
public class Test {

    public static void main(String[] args) {

        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        Thread t2 = new Thread(myRunnable);
        Thread t3 = new Thread(myRunnable);

        t1.start();
        t2.start();
        t3.start();
    }
}
 class MyObject {
     //将枚举类作为MyObject的内部类,这样可以避免因枚举类暴露而违反"职责单一原则"
     public enum MyEnumSingleton{
         connectionFactory;
         //提供一个Connection类型的变量
            private Connection connection;
            //构造方法私有化
            private MyEnumSingleton() {
                 try {
                    System.out.println("创建MyObject对象");
                     String driverName = "com.mysql.jdbc.Driver";
                     String url = "jdbc:mysql://localhost:3306/mydb";
                     String name = "root";
                     String password = "";
                     Class.forName(driverName);
                     connection = DriverManager.getConnection(url, name, password);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            //提供一个公有的获取Connection类型的方法
            public Connection getConnection() {
                return connection;
            }

     }
    //提供一个公有的静态的获取Connection类型的方法,在该方法中通过枚举类中的变量connectionFactory调用公有的获取Connection类型的方法
     public static Connection getConnection()
     {
        return MyEnumSingleton.connectionFactory.getConnection();

     }

}

/*
 * 通过实现Runnable接口实现多线程
 */
class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println(MyObject.getConnection().hashCode());

    }

}

运行结果如图 4-1所示
这里写图片描述
图 4-1控制台打印的hasCode是同一个值,说明对象是同一个,也就说明使用enum枚举数据类型实现了单例。


接着介绍序列化与反序列化的单例模式实现:
静态内部类可以达到线程安全问题,但如果遇到序列化对象时,使用默认的方式运行得到的结果还是多例的。
下面进行演示:

package com.javapatterns.singleton;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SaveAndRead {
    public static void main(String[] args) {
        try {
            MyObject mObject = MyObject.getInstance();
            FileOutputStream fosRef = new FileOutputStream(new File("myObjectFile.txt"));
            ObjectOutputStream oosRef = new ObjectOutputStream(fosRef);
            oosRef.writeObject(mObject);
            oosRef.close();
            fosRef.close();
            System.out.println(mObject.hashCode());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            FileInputStream fisRef = new FileInputStream(new File("myObjectFile.txt"));
            ObjectInputStream iosRef = new ObjectInputStream(fisRef);
            MyObject mObject = (MyObject) iosRef.readObject();
            iosRef.close();
            fisRef.close();
            System.out.println(mObject.hashCode());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

class MyObject implements Serializable {
    private static final long serialVersionUID = 888L;

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

    private MyObject() {

    }

    public static MyObject getInstance() {
        return MyObjectHandler.myObject;
    }
    // protected Object readResolve()
    // {
    // System.out.println("调用了readResolve方法!");
    // return MyObjectHandler.myObject;
    //
    // }
}

运行结果如图 4-2所示
这里写图片描述
图 4-2 不是同一个对象

解决办法就是在反序列化中使用readResolve()方法。

去掉如下代码的注释:

protected Object readResolve() {
        System.out.println("调用了readResolve方法!");
        return MyObjectHandler.myObject;

    }

去掉后运行结果如图 4-3所示
这里写图片描述
图 4-3 是同一个对象


(正在学习高洪岩先生著的《java多线程编程核心技术》,例子摘自此书,有兴趣的可以查阅此书)

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