設計模式----單例模式

什麼是單例模式

  在系統中,一個類只有一個實例,且自己提供這個實例。
  注意:
     1. 單例類只能有一個實例。
     2. 單例類必須自己創建自己的唯一實例。
     3. 單例類必須給所有其他對象提供這一實例。

單例程序

懶漢式:

package singleton.source.withOutThread;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
 class LazySingleton {

    //延遲加載
    private static LazySingleton singleton = null;

    private LazySingleton(){}

    public static LazySingleton getSingleton(){
        if( singleton == null){
            //測試多線程打開
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

class TestLazySingleton{
    public static void main(String[] args) {
//        testWithoutThread(10);   //單線程測試
        testWithThread(10);   //多線程測試
    }

    public static void testWithoutThread(int count){
        for (int i = 0; i < count; i++) {
            LazySingleton singleton = LazySingleton.getSingleton();
            System.out.println(singleton.hashCode());
        }
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(LazySingleton.getSingleton().hashCode());
                }
            }).start();
        }
    }
}

結論

  懶漢式在多線程環境下不是安全的!它的優點是在需要的時候加載,即延遲加載!
 

餓漢式:

package singleton.source.withOutThread;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
public class HungrySingleton {

    private static final HungrySingleton singleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getSingleton() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return singleton;
    }
}

class TestHungrySingleton{
    public static void main(String[] args) {
//        testWithoutThread();
        testWithThread(10);
    }

    public static void testWithoutThread(){
        HungrySingleton singleton1 = HungrySingleton.getSingleton();
        HungrySingleton singleton2 = HungrySingleton.getSingleton();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(HungrySingleton.getSingleton().hashCode());
                }
            }).start();
        }
    }
}

結論

  餓漢式在多線程環境下線程安全!因爲本類的實例化實在類加載時創建,也因此有一個天生缺陷,如果用不到該實例,會造成資源浪費。


以下開始,均爲線程安全。前提是,排除使用反射創建實例。若要避免反射創建攻擊,在私有構造器添加檢查標記即可。


雙重檢查鎖:

package singleton.source.withThread;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
public class DoubleCheckLock {

    //延遲加載
    //volatile 防止指令重排
    private static volatile DoubleCheckLock singleton = null;

    private DoubleCheckLock(){}

    public static DoubleCheckLock getSingleton(){
        if( singleton == null){
            synchronized (DoubleCheckLock.class) {
                if (singleton == null)
                    singleton = new DoubleCheckLock();
            }
        }
        return singleton;
    }

}

class TestDoubleCheckLock{
    public static void main(String[] args) {

//        testWithoutThread();
        testWithThread(1000);
    }

    public static void testWithoutThread(){
        SynLockSingleton singleton1 = SynLockSingleton.getSingleton();
        SynLockSingleton singleton2 = SynLockSingleton.getSingleton();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(DoubleCheckLock.getSingleton().hashCode());
                }
            }).start();
        }
    }
}

靜態內部類:

package singleton.source.withThread;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
public class InnerStaticSingleton {

    private static class SingletonInstance{
        private static final InnerStaticSingleton instance = new InnerStaticSingleton();
    }

    public static InnerStaticSingleton getInstance(){
//        try {
//            Thread.sleep(500);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        return SingletonInstance.instance;
    }
}

class TestInnerStaticSingleton{
    public static void main(String[] args) {

//        testWithoutThread();
        testWithThread(10);
    }

    public static void testWithoutThread(){
        InnerStaticSingleton singleton1 = InnerStaticSingleton.getInstance();
        InnerStaticSingleton singleton2 = InnerStaticSingleton.getInstance();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(InnerStaticSingleton.getInstance().hashCode());
                }
            }).start();
        }
    }
}


可序列化的單例:

package singleton.source.withThread;

import java.io.*;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
public class InnerStaticSingletonSerializable implements Serializable {

    private static final long serialVersionUID = 1L;

    //內部類
    private static class SingletonInstance{
        private static final InnerStaticSingletonSerializable instance = new InnerStaticSingletonSerializable();
    }

    public static InnerStaticSingletonSerializable getInstance(){
        return InnerStaticSingletonSerializable.SingletonInstance.instance;
    }


    protected Object readResolve(){
        System.out.println("readResolve被調用,防止序列化時多例");
        return InnerStaticSingletonSerializable.SingletonInstance.instance;
    }


}

class TestSynLockSingletonSerializable{
    public static void main(String[] args) {
        testWithSerializable();
    }

    public static void testWithSerializable(){

        InnerStaticSingletonSerializable singleton1 = InnerStaticSingletonSerializable.getInstance();

        File file = new File("designmodel\\src\\singleton\\source\\test.txt");

        try {
            if(!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter =new FileWriter(file);
            fileWriter.write("");
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(singleton1);
            oos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(oos != null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        FileInputStream fis = null;
        ObjectInputStream ois = null;
        InnerStaticSingletonSerializable singleton2 = null;
        try {
            fis = new FileInputStream(file);
            ois = new ObjectInputStream(fis);
            singleton2 = (InnerStaticSingletonSerializable) ois.readObject();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithoutThread(){
        InnerStaticSingletonSerializable singleton1 = InnerStaticSingletonSerializable.getInstance();
        InnerStaticSingletonSerializable singleton2 = InnerStaticSingletonSerializable.getInstance();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(InnerStaticSingletonSerializable.getInstance().hashCode());
                }
            }).start();
        }
    }
}

枚舉類單例(JDK1.5之後使用。天生線程安全。這貨不怕反射):

package singleton.source.withThread;

/**
 * @author hw
 * @createTime 2018/9/4
 * @dscrb
 */
public class SingletonEnumDemo{
    //枚舉類不要暴露,否則違反了“職責單一原則”
    private enum SingletonEnum {
        SINGLETONFACETORY;

        private MySingleton mySingleton = null;

        //枚舉類的構造方法在類加載是被實例化
        private SingletonEnum(){
            mySingleton = new MySingleton();
        }
    }

    public static MySingleton getInsatance(){
        return SingletonEnum.SINGLETONFACETORY.mySingleton;
    }
}


class MySingleton{
    //需要獲實現單例的類,比如數據庫連接Connection
    public MySingleton(){}
}

class TestMySingleton{
    public static void main(String[] args) {

//        testWithoutThread();
        testWithThread(10);
    }

    public static void testWithoutThread(){
        MySingleton singleton1 = SingletonEnumDemo.getInsatance();
        MySingleton singleton2 = SingletonEnumDemo.getInsatance();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SingletonEnumDemo.getInsatance().hashCode());
                }
            }).start();
        }
    }
}

鎖:

package singleton.source.withThread;

/**
 * @author hw
 * @createTime 2018/9/3
 * @dscrb
 */
public class SynLockSingleton {

    //延遲加載
    private static SynLockSingleton singleton = null;

    private SynLockSingleton(){}

    public static synchronized SynLockSingleton getSingleton(){
        if( singleton == null){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton = new SynLockSingleton();
        }
        return singleton;
    }

}

class TestSynLockSingleton{
    public static void main(String[] args) {

//        testWithoutThread();
        testWithThread(10);
    }

    public static void testWithoutThread(){
        SynLockSingleton singleton1 = SynLockSingleton.getSingleton();
        SynLockSingleton singleton2 = SynLockSingleton.getSingleton();
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }

    public static void testWithThread(int count){
        for (int i = 0; i < count; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(SynLockSingleton.getSingleton().hashCode());
                }
            }).start();
        }
    }
}

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