單例模式與多線程(二)

在單例模式與多線程(一)中,介紹了四種在多線程環境下實現的單例模式:餓漢模式、懶漢模式、靜態內部類實現方式和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多線程編程核心技術》,例子摘自此書,有興趣的可以查閱此書)

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