单例模式与多线程(二)

来源:互联网 发布:php程序员的工作常态 编辑:程序博客网 时间:2024/05/28 06:04

在单例模式与多线程(一)中,介绍了四种在多线程环境下实现的单例模式:饿汉模式、懒汉模式、静态内部类实现方式和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多线程编程核心技术》,例子摘自此书,有兴趣的可以查阅此书)

原创粉丝点击