博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Java中的單例模式、多線程安全問題以及死鎖

Posted on 2014-02-22 17:16  偶尔不跑调  阅读(162)  评论(0)    收藏  举报

單例模式,Java眾多設計模式中的一種,分為餓漢式和懶漢式(廷遲加載),簡要代碼如下:

饿漢式:

1 //餓漢式
2 public class Singleton {
3     private static final Singleton singleton = new Singleton();
4     private Singleton() {}
5     public static Singleton getInstance() {
6         return singleton;
7     }
8 }


懶漢式(廷遲加載),因多線程訪問時存在線程安全問題,故應加同步鎖。如下:

 1 //懶漢式 方式1
 2 public class Singleton {
 3     private static Singleston singleton = null;
 4     private Singleton() {}
 5     //把鎖加在方法聲明上,變成同步函數
 6     public static synchronized Singleton getInstance() {
 7         if (singleton == null) {
 8             singleton = new Singleton();
 9         }
10         return singleton;
11     }
12 }
13 //方式1每次執行到getInstance()方法時都要檢查同步鎖,對程序運行性能的影響較大。

 

 1 //懶漢式 方式2
 2 public class Singleton {
 3     private static Singleston singleton = null;
 4     private Singleton() {}
 5     public static Singleton getInstance() {
 6         if (singleton == null) {
 7             //把鎖加在可能產生線程安全問題的代碼上,變成同步代碼塊
 8             synchronized(Singleton.class) {
 9                 if (singleton == null) {
10                     singleton = new Singleton();
11                 }
12             }
13         }
14         return singleton;
15     }
16 }
17 //當同步代碼塊所在方法getInstance()非靜態時,synchronized(Singleton.class){}應改為synchronized(this){}。   
18 //即靜態時用該類的字節碼文件為參數,非靜態時用該類的實例對象為參數。

 

在實際開發中一般推薦使用餓漢式,因其代碼相對簡潔,且不存在多線程訪問的線程安全問題。

 

-------------------------------------------------------------------------------------------------------------------------

多線程訪問同一份共享資源時,可能產生線程安全問題,於是給共享資源加鎖,但這同時又會帶來另一個問題,就是死鎖。
何謂死鎖?死鎖是指兩個或兩個以上的線程在執行過程中因爭奪資源而造成互不相讓、互相等待的現象。
死鎖產生的條件有四個:
1.互斥條件:線程在某一時間內獨佔資源。
2.請求與保持條件:一個線程因請求資源而阻塞時,對已獲資源不放。
3.不剝奪條件:線程已獲得資源,在末使用完之前,不能強行剝奪。
4.循環等待條件:若干線程之間形成一種頭尾相接的循環等待資源關係。
形象點說,就是線程1鎖定了A資源又想去鎖定B資源,同時線程2鎖定了B資源又想去鎖定A資源,兩線程互不相讓,相持不下的現象。
簡單來說,就是同步中嵌套同步,而鎖卻不同。
多線程死鎖的簡要代碼如下:

 1 //模擬多線程死鎖現象
 2 public class ThreadDeadLock implements Runnable {
 3     private boolean flag;
 4     ThreadDeadLock(boolean flag) {
 5         this.flag = flag;
 6     }
 7     
 8     private static Object lock1 = new Object(); //定義兩個鎖,注意加static
 9     private static Object lock2 = new Object();
10     
11     public void run() {
12         if (flag) {
13             synchronized(lock1) {
14                 System.out.println(Thread.currentThread().getName() + " -- true -- A"); //t1線程獲得資源A
15                 synchronized(lock2) {
16                     System.out.println(Thread.currentThread().getName() + " -- true -- B"); //t1線程獲得資源B
17                 }
18             }
19         } else {
20             synchronized(lock2) {
21                 System.out.println(Thread.currentThread().getName() + " -- false -- B"); //t2線程獲得資源B
22                 synchronized(lock1) {
23                     System.out.println(Thread.currentThread().getName() + " -- false -- A"); //t2線程獲得資源A
24                 }
25             }
26         }
27     }
28     
29     public static void main(String[] args) {
30         Thread t1 = new Thread(new ThreadDeadLock(true)); //新建兩個線程
31         Thread t2 = new Thread(new ThreadDeadLock(false));
32         t1.start(); //啟動線程
33         t2.start();
34     }
35 }


運行結果如下(參考):

 

在使用synchronized解決Java多線程同步問題時應避免出現死鎖現象,只要破壞死鎖產生的四個條件中的一個即可。