挑战常规,这样写单例是错的!

640?wx_fmt=png

作者 :我想嘿嘿

来源:https://www.cnblogs.com/loveheihei

说到单例,网上教程和很多人信手拈来:

public class Single
{
    private volatile static Single instance;

    private Single()
    {
        System.out.println("创建单例");
    }
    public static Single getInstance()
    {
        if (instance == null)
        {
            synchronized (Single.class)
            {
                if (instance == null)
                {
                    instance = new Single();
                }
            }
        }
        return instance;
    }
}

自信满满,称之为懒汉加载模式,言之节省内存,用时才会自动创建。在我看来,这种写法完全是错误的,愚蠢至极,这不是脱裤子放屁,这是脱完裤子再提取裤子再放屁。

正确写法就是最简单的:

public class Single
{
    private   static Single instance=new Single();

    private Single()
    {
        System.out.println("创建单例");
    }
    public static Single getInstance()
    {
        return instance;
    }
}

是不是用synchronized创建单例就没有用处了呢?

并不是!synchronized是用于解决多线程访问问题。带参数的单例创建才应该使用懒汉模式。

因为并不能预期,什么时候参数被传入。简单模式下并不清楚传入什么参数,或参数对象未初始化。

public class Single
{
    private volatile static Single instance ;  
    public Single(Context context)
    {
        System.out.println("创建单例");
    }
    public static Single getInstance(Context context)
    {
        if (instance == null)
        {
            synchronized (Single.class)
            {
                if (instance == null)
                {
                    instance = new Single(context);
                }
            }
        }
        return instance;
    }
}

为什么说这个是为了解决多线程访问呢,先看如果不加锁会发生什么。

public class Single
{
    private static Single instance ;   
    public Single(Context context)
    {
        System.out.println("创建单例");
    }
    public static Single getInstance(Context context)
    {
        if (instance == null)
        {
            instance = new Single(context);
        }
        return instance;
    }
}
public class SingleTest
{

    public static void main(String[] args) throws Exception
    {
        ExecutorService pool = Executors.newCachedThreadPool();
        ArrayList<Callable<Void>> runners=new   ArrayList<>();
        for(int i=0;i<10;i++)
        {
            runners.add(()->{
                Single.getInstance(new Context());
                return null;
            });
        }
        System.out.println("启动线程");
        pool.invokeAll(runners);
        pool.shutdown();
        System.in.read();
    }
}

不加锁情况,控制台输出:

启动线程
创建单例
创建单例
创建单例
创建单例

加锁情况,输出:

启动线程
创建单例

总结,无参构造单例无需复杂的加入synchronized,而未确定的传参单例需要加synchronized保证多线程访问安全。

思考,如果传入的参数确定,怎么写才最优呢?下面类似写法是否合理:

public class Single
{
    private   static Single instance =new Single(Context.instance); 

    public Single(Context context )
    {
        System.out.println("创建单例");
    }
    public static Single getInstance( )
    {

        return instance;
    }
}


推荐阅读


技术之路从来没那么简单。一个人可以跑得更快,但一个人可以跑得更远。扫描下方二维码添加小助手微信,回复「入群」与两千小伙伴一起共同前行。

640?wx_fmt=png

640?wx_fmt=png

公众号@Java技术精选,关注 Java 程序员的个人成长,分享最新技术资讯与技术干货。与你成长有关的,我们这里都有。

↑↑创作不易,如果喜欢请转发↑↑


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值