欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-02 14:11:47
说到单例,网上教程和很多人信手拈来: 自信满满,称之为懒汉加载模式,言之节省内存,用时才会自动创建。在我看来,这种写法完全是错误的,愚蠢至极,这不是脱裤子放屁,这是脱完裤子再提取裤子再放屁。 正确写法就是最简单的: 下面驳斥所谓的省内存。 首先,不用单例时,难道别的写法就会加载单例?没有引用使用单例 ......

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

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;
	}
}

 

下面驳斥所谓的省内存。

public class singletest
{
 
	public static void main(string[] args) throws  exception
	{ 
		system.out.println("启动线程");
		system.in.read();
	}

}

 首先,不用单例时,难道别的写法就会加载单例?没有引用使用单例类时,当然都不会加载单例。

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

看图,并不会加载创建单例,控制台也不会输出创建单例。

其次,既然预定要使用单例,那么都会加载创建一次。

public class singletest
{
 
	public static void main(string[] args) throws  exception
	{ 
		system.out.println("启动线程");
		single.getinstance();
		system.in.read();
	}

}

 看图,无论哪种模式单例都会被引用加载

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

 

是不是用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;
	}
}

 

 

@weblistener
public class myservletcontextlistener implements servletcontextlistener {
  
    public void contextdestroyed(servletcontextevent sce)  { 
      
    }
 
    public void contextinitialized(servletcontextevent sce)  { 
        single.getinstance(sce);
    }
    
}