JAVA中正确使用的单例模式常用写法(6种写法)

星期六, 2017-11-04 | Author: Lee | JAVA-and-J2EE | 351 views

单例模式 老生常谈的一种模式,也是实际项目代码中比较常用的一种模式了.

文中做了6种方法,实际用的最多的也就 4.5两种,其实6是最完美的但是感觉别捏呢,不像个类,用的也并不多.

1.懒汉式,线程不安全

public class Singleton {
    private static Singleton instance;
    private Singleton (){}
    public static Singleton getInstance() {
     if (instance == null) {
         instance = new Singleton();
     }
     return instance;
    }
}

2.懒汉式,线程安全 (BUT.效率差)

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

3.双重检验锁

public static Singleton getSingleton() {
    if (instance == null) {                         //Single Checked
        synchronized (Singleton.class) {
            if (instance == null) {                 //Double Checked
                instance = new Singleton();       //非原子操作
            }
        }
    }
    return instance ;
}

问题:new Singleton非原子操作
1.给 instance 分配内存
2.调用Singleton 的构造函数来初始化成员变量
3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)

解决办法 JDK1.5以前的版本会有问题, JDK1.5后用volatile关键字(立即可见及禁止指令重排序优化)来修饰变量.通过DCL(double checked locking)实现同步

public class Singleton {
    private volatile static Singleton instance; //这里为 volatile
    private Singleton (){}
    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }  
}

上面三种的写法基本上在项目中没有什么实用价值,下面的三种才是比较常用的

4.饿汉式 static final field,(无参数推荐使用)

public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();   
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

缺点:依赖参数或者配置文件无法在类初始的时候进行;
如果无参数或者要加载配置文件可以直接使用比较方便

5.静态内部类 static nested class (通用 推荐使用)

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

6.枚举 Enum (可以实现接口的方式使用,感觉不像类文件,使用也不多)

public enum Singleton {
    INSTANCE {
        @Override
        protected void read() {
            System.out.println("read");
        }
 
        @Override
        protected void write() {
            System.out.println("write");
        }
 
    };
    protected abstract void read();
    protected abstract void write();
}

注:优点:
1.线程安全
默认枚举实例的创建是线程安全的.(创建枚举类的单例在JVM层面也是能保证线程安全的),所以不需要担心线程安全的问题

2.不会因为序列化而产生新实例,枚举单例 对序列化有保证
以往的单例实现了序列化接口,那么就再也不能保持单例的状态了.因为readObject()方法一直返回一个新的对象.使用radResolve()来避免此情况发生.

  //readResolve to prevent another instance of Singleton
    private Object readResolve(){
        return INSTANCE;
    }

3.防止反射攻击
采用反射来创建实例时.可通过AccessibleObject.setAccessible(),通过反射机制来调用私有构造器.那么枚举可以防止这种创建第二个实例的情况发生.

缺点:不像在使用一个CLASS的方式,使用起来感觉别捏,实际看到用的也并不多,也许以后会有很多人用(谁知道呢…)

Tags: ,

文章作者: Lee

本文地址: https://www.pomelolee.com/1772.html

除非注明,Pomelo Lee文章均为原创,转载请以链接形式标明本文地址

No comments yet.

Leave a comment

Search

文章分类

Meta