java常用设计模式--单例模式

单例模式:程序从系统开始到结束只产生唯一的实例,并且提供该单例的全局访问点,尤其在系统程序中出现功能性地冲突时,就需要用到单例模式。

常见的单例有:日历、配置文件、IOC容器等。

单例的写法分为:饿汉式、懒汉式、注册式、序列化

饿汉式:是指该实例没被调用前,程序运行了系统就初始化保持该实例,就显得比较饥饿,迫不及待,该方式如果系统存在的单例比较多,在系统初始化时就比较消耗内存。

/**
 * 饿汉式单例
 */
public class HungrySingleton {

    private HungrySingleton(){}

    private final static HungrySingleton INSTANCE = new HungrySingleton();

    public final static HungrySingleton getInstance(){
        return INSTANCE;
    }

}

懒汉式:是指该实例被初次调用时才去实例化并保存,就显得比较懒,但是这种就避免了饿汉式内存消耗的问题。

/**
 * 懒汉式
 */
public class LazySingleton {

    private LazySingleton(){}

    private static LazySingleton INSTANCE = null;

    public final static LazySingleton getInstance(){
        if(INSTANCE==null){
            INSTANCE = new LazySingleton();
        }
        return INSTANCE;
    }


}

上面这种单例写法容易发生并发线程安全问题,可以写个发令枪计数器模拟下线程并发的情况,发现并发时候,在INSTANCE为null时,多个线程同时访问时,会出现INSTANCE==null都判断为true。模拟代码:

public class SingletonTest {

    public static void main(String[] args) {

        /**
         * 单例测试
         */
        int count = 200;

        final CountDownLatch latch = new CountDownLatch(count);

        for (int i = 0; i < count; i++) {
            new Thread(){
                @Override
                public void run() {
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    LazySingleton singleton = LazySingleton.getInstance();
                    System.out.println(System.currentTimeMillis()+"->"+singleton);
                }
            }.start();
            latch.countDown();
        }
    }
}

在懒汉式获取单例的方法加上synchronized关键字无疑能很好的解决线程安全的问题,但是同样会引来了性能损耗的问题(线程并发访问getInstance()就会出现阻塞问题)。

/**
 * 懒汉式
 */
public class LazySingleton {

    private LazySingleton(){}

    private static LazySingleton INSTANCE = null;

    public synchronized final static LazySingleton getInstance()  {
        if(INSTANCE==null){
            INSTANCE = new LazySingleton();
        }
        return INSTANCE;
    }
}

利用静态内部类方式:

/**
 * 懒汉式
 */
public class LazySingleton {

    private LazySingleton(){}

    //static提供全局访问
    //final防止被重写重载
    public final static LazySingleton getInstance()  {
        return LazyHolder.INSTANCE;
    }
    //利用静态内部类方式,默认不加载,LazySingleton被初始化时才加载,
    // 这样既保证饿汉式的内存损耗问题,又可以友好地避免线程安全问题
    private static class LazyHolder{
        private final static LazySingleton INSTANCE = new LazySingleton();
    }
}

注册式:是指将单例对象注册到容器中或者利用枚举方式。

/**
 * 注册式 -- 容器
 */
public class RegisterSingleton {

    private final static Map container = new HashMap();

    private RegisterSingleton(){}

    public final static RegisterSingleton getInstance(String name){
        if(name == null) {
            name = RegisterSingleton.class.getName();
        }

        if(container.get(name) == null){
            container.put(name,new RegisterSingleton());
        }

        return (RegisterSingleton)container.get(name);
    }
}
/**
 * 注册式--枚举
 */
public enum RegisterEnum {
    INSTANCE
}

序列化:可以利用序列化将对象写入文件或者流中,再用反序列化读取文件或者流得到该对象。

/**
 * 序列化
 */
public class SerialSingleton implements Serializable {

    private static final long serialVersionUID = 5845248228939246644L;

    private SerialSingleton(){}

    private final static SerialSingleton INSTANCE = new SerialSingleton();

    public final static SerialSingleton getInstance()  {
        return INSTANCE;
    }

    private Object readResolve(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        SerialSingleton s1 = getInstance();
        SerialSingleton s2 = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(s1);
            oos.flush();
            oos.close();

            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            s2 = (SerialSingleton)ois.readObject();
            System.out.println(s1 == s2); //true

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

单例反射入侵:由于反射机制,单例的模式都会存在被破坏问题,这无疑会对单例产生入侵问题,我们可以在该单例的构造里判断改单例是否被初始化,被初始化了让它抛出异常即可。

/**
 * 饿汉式单例
 */
public class HungrySingleton {

    private static boolean isInitialed = false;

    private HungrySingleton(){
        synchronized (HungrySingleton.class){
            if(isInitialed){
                isInitialed = !isInitialed;
            }else{
                throw new RuntimeException("this singleton has been initialed!");
            }
        }
    }

    private final static HungrySingleton INSTANCE = new HungrySingleton();

    public final static HungrySingleton getInstance(){
        System.out.println(System.currentTimeMillis()+"->"+INSTANCE);
        return INSTANCE;
    }

}

 

 

你可能感兴趣的:(java设计模式)