解決單例設計模式中懶漢式線程安全問題


 首先寫個單例:

 

public class SingleDemo {  
    private static SingleDemo s = null;  
    private SingleDemo(){}  
    public static  SingleDemo getInstance(){  
        if(s == null){  
            s = new SingleDemo();  
        }  
        return s;  
    }  
}  

  


寫個測試類:

 

 

public class ThreadDemo3 {  
      
    public static void main(String[] args) {  
        SingleDemo s1 = SingleDemo.getInstance();  
        SingleDemo s2 = SingleDemo.getInstance();  
        System.out.println(s2 == s2);  
    }  
}  

  

運行結果一直都是true,說明單線程下是沒問題的,下面寫個多線程來訪問單例

 

 

public class ThreadTest implements Runnable {  
    //存放單例對象,使用Set是為了不存放重復元素  
    public Set<SingleDemo> singles = new HashSet<SingleDemo>();  
    @Override  
    public void run() {  
        //獲取單例  
        SingleDemo s = SingleDemo.getInstance();  
        //添加單例  
        singles.add(s);  
    }  
}  

  


使用多線程並發訪問單例:

 

 

public class ThreadDemo3 {  
      
    public static void main(String[] args) {  
//      SingleDemo s1 = SingleDemo.getInstance();  
//      SingleDemo s2 = SingleDemo.getInstance();  
//      System.out.println(s2 == s2);  
        ThreadTest t = new ThreadTest();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        System.out.println(t.singles);  
    }  
}  

//看不出結果改成下面這樣
public class ThreadDemo3 {  
    
    public static void main(String[] args) {  
//      SingleDemo s1 = SingleDemo.getInstance();  
//      SingleDemo s2 = SingleDemo.getInstance();  
//      System.out.println(s2 == s2);  
        ThreadTest t = new ThreadTest();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        new Thread(t).start();  
        try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        System.out.println(t.singles);  
    }  
}  

  

運行結果如下:

 

[com.persagy.thread.SingleDemo@1bc4459, com.persagy.thread.SingleDemo@150bd4d]

[com.persagy.thread.SingleDemo@12b6651]

說明有線程並發訪問安全問題,獲取的不一定都是同一個實例

如何解決線程安全問題呢?

當然使用同步鎖機制了啊

//線程安全懶漢

下面改進單例:

 

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

 


加入同步函數后線程安全問題解決了

 

運行多次都是獲取同一個實例,不會出現2個實例的情況了

[com.persagy.thread.SingleDemo@12b6651]

但是在多線程並發訪問的情況下,每個線程每次獲取實例都要判斷下鎖,效率比較低,為了提高效率,我加入了雙重判斷的方法,解決了效率的問題

代碼如下;

 //雙重校驗鎖

public class SingleDemo {  
    private static SingleDemo s = null;  
    private SingleDemo(){}  
    public static  SingleDemo getInstance(){  
        /*如果第一個線程獲取到了單例的實例對象, 
         * 后面的線程再獲取實例的時候不需要進入同步代碼塊中了*/  
        if(s == null){  
            //同步代碼塊用的鎖是單例的字節碼文件對象,且只能用這個鎖  
            synchronized(SingleDemo.class){  
                if(s == null){  
                    s = new SingleDemo();  
                }  
            }  
        }  
        return s;  
    }  
}  

  


用這種方式解決了懶漢式的線程安全問題,也提高了效率,但是在實際開發中還是用餓漢式的比較多,畢竟這個代碼比較多,比較繁瑣。
 
 
下面是大神的單例http://www.hollischuang.com/archives/1373


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM