博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单例模式的七种实现-Singleton(Java实现)
阅读量:4655 次
发布时间:2019-06-09

本文共 6166 字,大约阅读时间需要 20 分钟。

1. 饿汉式

实现代码:

public class Singleton {    private Singleton() {    }    private static Singleton singleton = new Singleton();    public static Singleton getInstance() {        return singleton;    }}

验证一下:

public static void main(String[] args) {        Singleton s1 = Singleton.getInstance();        Singleton s2 = Singleton.getInstance();        System.out.println(s1 == s2);        // true    }

 

如果用反射, 是否仍然是单例:

结果是反射破坏了单例

public static void main(String[] args) throws Exception {        // 自定义单例方法获取        Singleton s1 = Singleton.getInstance();        // 反射获取        Constructor constructor = Singleton.class.getDeclaredConstructor();        constructor.setAccessible(true);        Singleton s2 = (Singleton) constructor.newInstance();        System.out.println(s1 == s2);        //false    }

2. 懒汉式

将上面的饿汉式改为懒汉式:

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

 

验证一下:

public static void main(String[] args) {        Singleton s1 = Singleton.getInstance();        Singleton s2 = Singleton.getInstance();        System.out.println(s1 == s2);        // true    }

 

不过这是一种线程不安全的单例实现.

我们在Singleton中加上sleep来模拟一下线程切换:

public class Singleton {    private Singleton() {    }    private static Singleton singleton;    public static Singleton getInstance() {        if (singleton == null) {            try {                Thread.sleep(0);            } catch (InterruptedException e) {                e.printStackTrace();            }            singleton = new Singleton();        }        return singleton;    }}

 验证一下线程不安全:

public class Main3 {    private static LinkedBlockingQueue
singletons = new LinkedBlockingQueue<>(); public static void main(String[] args) throws Exception{ ExecutorService threadPool = Executors.newFixedThreadPool(10); for(int i= 0;i<100;i++){ threadPool.execute(()->{ singletons.offer(Singleton.getInstance()); }); } Singleton basic = singletons.take(); while(basic==singletons.take()){ System.out.println("continue"); continue; } System.out.println("走到这里说明单例失败"); }}

 

3. 懒汉式+同步方法

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

 验证一下:

public static void main(String[] args) {        Singleton s1 = Singleton.getInstance();        Singleton s2 = Singleton.getInstance();        System.out.println(s1 == s2);        // true    }

  由于是同步, 所以同步方法内不会出现多线程执行的情况.

4. 懒汉式+双重校验锁

因为上面那种会每次进时都会进行同步锁, 很浪费性能, 所以在加锁之间先进行校验

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

 验证一下性能:

能明显看出来性能差距...5千倍...

同步方法, 即直接在方法声明处加了Synchronize的情况:

public static void main(String[] args){        long start = System.currentTimeMillis();        for(int i= 0;i<999999999;i++){            Singleton s = Singleton.getInstance();        }        System.out.println(System.currentTimeMillis()-start);    }

 

双重校验锁:

public static void main(String[] args){        long start = System.currentTimeMillis();        for(int i= 0;i<999999999;i++){            Singleton s = Singleton.getInstance();        }        System.out.println(System.currentTimeMillis()-start);    }

 

5. 懒汉式+双重校验锁+防止指令重拍

看似简单的一段赋值语句:instance = new Singleton(); 其实JVM内部已经转换为多条指令:

memory = allocate(); //1:分配对象的内存空间

ctorInstance(memory); //2:初始化对象

instance = memory; //3:设置instance指向刚分配的内存地址

但是经过重排序后如下:

memory = allocate(); //1:分配对象的内存空间

instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化

ctorInstance(memory); //2:初始化对象

可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。

加上volatile关键字:

public class Singleton {    private Singleton() {    }    private volatile static Singleton singleton;    public static Singleton getInstance() {        if (singleton == null) {            synchronized (Singleton.class) {                if (singleton == null) {                    singleton = new Singleton();                }            }        }        return singleton;    }}

 验证一下性能:

能明显看出来加了volatile后对性能的影响, 由之前的5, 变为了302...

性能下降了, 但是相比于上面的双重校验锁, 更保证了线程安全.

public static void main(String[] args){        long start = System.currentTimeMillis();        for(int i= 0;i<999999999;i++){            Singleton s = Singleton.getInstance();        }        System.out.println(System.currentTimeMillis()-start);    }

 

6. 静态内部类

这也是一种很好的实现方式, 不仅懒加载, 还保证了线程安全, 性能也很好, 实现起来也很简单

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

 验证一下性能:

public static void main(String[] args){        long start = System.currentTimeMillis();        for(int i= 0;i<999999999;i++){            Singleton s = Singleton.getInstance();        }        System.out.println(System.currentTimeMillis()-start);    }

 

7. 枚举

个人对枚举类型的理解还有限, 有待学习....

public enum Singleton {    INSTANCE;    private String name;    Singleton() {        this.name = "king";    }    public static Singleton getInstance() {        return INSTANCE;    }    public String getName() {        return this.name;    }}

 验证一下性能:

public static void main(String[] args){        long start = System.currentTimeMillis();        for(int i= 0;i<999999999;i++){            Singleton s = Singleton.getInstance();        }        System.out.println(System.currentTimeMillis()-start);    }

 

 

转载于:https://www.cnblogs.com/noKing/p/java_design_patterns_Singleton.html

你可能感兴趣的文章
引入css的四种方式
查看>>
iOS开发UI篇—transframe属性(形变)
查看>>
3月7日 ArrayList集合
查看>>
jsp 环境配置记录
查看>>
Python03
查看>>
LOJ 2537 「PKUWC2018」Minimax
查看>>
使用java中replaceAll方法替换字符串中的反斜杠
查看>>
Some configure
查看>>
流量调整和限流技术 【转载】
查看>>
1 线性空间
查看>>
VS不显示最近打开的项目
查看>>
DP(动态规划)
查看>>
chkconfig
查看>>
2.抽取代码(BaseActivity)
查看>>
夏天过去了, 姥爷推荐几套来自smashingmagzine的超棒秋天主题壁纸
查看>>
反射的所有api
查看>>
css 定位及遮罩层小技巧
查看>>
[2017.02.23] Java8 函数式编程
查看>>
sprintf 和strcpy 的差别
查看>>
JS中window.event事件使用详解
查看>>