博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单例模式 分析 代码优化
阅读量:5295 次
发布时间:2019-06-14

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

  单例模式是23种设计模式之一,是比较简单的一种设计模式,它的目的是无论调用多少次,都返回同一个对象,它的特点是构造器私有化。

  它分为两种结构,一种是懒汉式的,一种是饿汉式的,它们各有优缺点,我们先从饿汉式看起,代码如下:

public class Single {    private static Single single = new Single();    private Single() {    }    public Single getInstance() {        return single;    }}

  通过上面的程序可以看出来虽然我们加载同一个对象的目的确实达到了,但当程序被加载的时候就会创建single这个对象,当这个类有多个这样的方法时,我们可能会用不到这个对象中大多数单例,就会造成对内存的浪费。所以就出现了懒汉式的单例模式,代码如下:

public class Single {    private static Single single = null;    private Single() {    }    public Single getInstance() {        if(single==null){            single = new Single();        }        return single;    }}

  这样,只有当我们真正调用这个对象时它才会被new出来,但是这样是存在问题的。

  当上面的第二段代码在第一次加载的时候有两个线程对其进行了调用,则会产生两个不同的对象,所以是线程不安全的,这时候就会想到给这个方法加个锁,加锁之后的代码如下:

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

  这样做确实做到了线程安全,但是当加锁这个方法里面要执行很多东西,调用这个方法花费的时间会很长,这样对服务器来说是致命的,因为这个方法如果某个线程一直调用的话,其它的线程是没有办法调的,服务器就阻塞了,那么升级后的代码如下:

public class Single {    priate static Single single = null;    private Single() {    }    public Single getInstance() {        if (single == null) {            synchronized (Single.class) {                single = new Single();            }        }        return single;    }}

  仔细观察以后发现这样并没有锁住,当第一次同时有两个线程到达getInstance()方法if判断时,其中有一个肯定是阻塞的,当另外一个执行完以后,阻塞这个线程是不会再判断是否为空的,还是会创建一个对象的,这样又有多个对象被产生了,再对其进行升级,得到的代码如下:

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

  这样就不会产生上面的问题,而且也只锁一次,因为第二次再执行这个方法时,会跳过if判断,直接返回single,不会再被锁,执行效率也会很高。

  但即使是这样,也还是有问题的,因为我们不能确定在内存中是先给对象赋值,还是先创建了这个对象,所以第二个程序有可能得到的是初始化一半了的对象,在jdk1.5之后,我们可以用volatile这个关键字来避免这种情况,代码如下:

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

  但是这种情况很少使用,我在这里只是为了学习一下,嘻嘻

最近有了一个新的写法:如下

public class TestSingleton {    private static class inner{        private static TestSingleton singleton = new TestSingleton();    }    private TestSingleton(){    }     public static TestSingleton getSingle(){         return inner.singleton;     }}

这种方法很不错的,这样避免了在加载类的时候就初始化,达到了懒加载的目的

比下面这种方式好一些

public class TestSingle2 {        private static TestSingle2 testSingleton ;        static {        testSingleton = new TestSingle2();    }        private TestSingle2(){            }        public static TestSingle2 getSingleton(){        return  testSingleton;    }}

这种方式就没有实现懒加载,

项目中主要还是应该用最下面两种方式

 

转载于:https://www.cnblogs.com/lilyjia/p/3876167.html

你可能感兴趣的文章
狄利克雷过程(Dirichlet Process)
查看>>
五子棋项目的实现(二)博弈树算法的描述
查看>>
Hibernate : Disabling contextual LOB creation as createClob() method threw error
查看>>
【bzoj4872】[Shoi2017]分手是祝愿 期望dp
查看>>
字符串元转分
查看>>
thinkphp 防sql注入
查看>>
201521123044 《Java程序设计》第1周学习总结
查看>>
MIT Scheme 的基本使用
查看>>
程序员的“机械同感”
查看>>
在16aspx.com上下了一个简单商品房销售系统源码,怎么修改它的默认登录名和密码...
查看>>
c++回调函数
查看>>
linux下Rtree的安装
查看>>
【Java】 剑指offer(53-2) 0到n-1中缺失的数字
查看>>
Delphi中ListView类的用法
查看>>
bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】
查看>>
[原创]Java 的传值小例子
查看>>
博客第一弹—聊聊HTML的那些事
查看>>
Mysql安装方法及安装问题解决
查看>>
Java动态代理的两种实现方式:
查看>>
PHP trait
查看>>