设计模式----单例模式

什么是单例模式

  在系统中,一个类只有一个实例,且自己提供这个实例。
  注意:
     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();
        }
    }
}

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