挑戰常規--這樣寫單例是錯的!

說到單例,網上教程和不少人信手拈來:java

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

 

下面駁斥所謂的省內存。spa

public class SingleTest
{
 
	public static void main(String[] args) throws  Exception
	{ 
		System.out.println("啓動線程");
		System.in.read();
	}

}

 首先,不用單例時,難作別的寫法就會加載單例?沒有引用使用單例類時,固然都不會加載單例。線程

看圖,並不會加載建立單例,控制檯也不會輸出建立單例。3d

其次,既然預約要使用單例,那麼都會加載建立一次。code

public class SingleTest
{
 
	public static void main(String[] args) throws  Exception
	{ 
		System.out.println("啓動線程");
		Single.getInstance();
		System.in.read();
	}

}

 看圖,不管哪一種模式單例都會被引用加載對象

 

是否是用synchronized建立單例就沒有用處了呢?blog

並非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);
    }
    
}
相關文章
相關標籤/搜索