線程(下)

    Java 應用程序中的多線程可以共享資源,例如文件、數據庫、內存等。當線程以併發模式訪問共享數據時,共享數據可能會發生衝突。Java引入線程同步的概念,以實現共享數據的一致性。線程同步機制讓多個線程有序的訪問共享資源,而不是同時操作共享資源。 

1  . 同步概念 

    在線程異步模式的情況下,同一時刻有一個線程在修改共享數據,另一個線程在讀取共享數據,當修改共享數據的線程沒有處理完畢,讀取數據的線程肯定會得到錯誤的結果。如果採用多線程的同步控制機制,當處理共享數據的線程完成處理數據之後,讀取線程讀取數據。 
     
通過分析多線程出售火車票的例子,可以更好得理解線程同步的概念。線程 Thread1 和線程 Thread2 都可以出售火車票,但是這個過程中會出現數據與時間信息不一致的情況。線程 Thread1 查詢數據庫,發現某張火車票 T 可以出售,所以準備出售此票;此時系統切換到線程Thread2執行,它在數據庫中查詢存票,發現上面的火車票T可以出售,所以線程Thread2將這張火車票 T 售出;當系統再次切換到線程 Thread1 執行時,它又賣出同樣的票 T。這是一個典型的由於數據不同步而導致的錯誤。 
     
下面舉一個線程異步模式訪問數據的例子。 

//文件:程序ThreadNoSynchronized.java   描述:多線程不同步的原因   
class ShareData {  
    public static String szData = ""; // 聲明,並初始化字符串數據域,作爲共享數據  
  
}  
class ThreadDemo extends Thread {  
    private ShareData oShare; // 聲明,並初始化ShareData 數據域  
    ThreadDemo() {  
    } // 聲明,並實現ThreadDemo 構造方法  
  
    // 聲明,並實現ThreadDemo 帶參數的構造方法  
    ThreadDemo(String szName, ShareData oShare) {  
        super(szName); // 調用父類的構造方法  
        this.oShare = oShare; // 初始化oShare域  
    }  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            if (this.getName().equals("Thread1")) {  
                oShare.szData = "這是第 1 個線程";  
                // 爲了演示產生的問題,這裏設置一次睡眠  
                try {  
                    Thread.sleep((int) Math.random() * 100); // 休眠  
                } catch (InterruptedException e) { // 捕獲異常  
                }  
                System.out.println(this.getName() + ":" + oShare.szData); // 輸出字符串信息  
            } else if (this.getName().equals("Thread2")) {  
                oShare.szData = "這是第 2 個線程";  
                // 爲了演示產生的問題,這裏設置一次睡眠  
                try {  
                    Thread.sleep((int) Math.random() * 100); // 線程休眠  
                } catch (InterruptedException e) // 捕獲異常  
                {  
                }  
                System.out.println(this.getName() + ":" + oShare.szData); // 輸出字符串信息  
            }  
        }  
    }  
}  
  
public class ThreadNoSynchronized {  
    public static void main(String argv[]) {  
        ShareData oShare = new ShareData(); // 創建,初始化ShareData對象oShare  
        ThreadDemo th1 = new ThreadDemo("Thread1", oShare); // 創建線程th1  
        ThreadDemo th2 = new ThreadDemo("Thread2", oShare); // 創建線程th2  
        th1.start(); // 啓動線程th1  
        th2.start(); // 啓動線程th2  
    }  
}  

 Thread1:這是第 2 個線程

Thread1:這是第 1 個線程
Thread1:
這是第 1 個線程
Thread1:
這是第 1 個線程
Thread1:
這是第 1 個線程
Thread2:
這是第 2 個線程
Thread2:
這是第 2 個線程
Thread2:
這是第 2 個線程
Thread2:
這是第 2 個線程
Thread2:
這是第 2 個線程

  程序中預想的結果是:“Thead1:這是第1 個線程“Thead2:這是第2 個線程,但是線程對數據的異步操作導致運行結果出現了差錯。  上面程序是由於線程不同步而導致錯誤。爲了解決此類問題,Java 提供了機制實現線程的同步。 

        鎖機制的原理是每個線程進入共享代碼之前獲得鎖,否則不能進入共享代碼區,並且在退出共享代碼之前釋放該鎖,這樣就解決了多個線程競爭共享代碼的情況,達到線程同步的目的。Java中鎖機制的實現方法是共享代碼之前加入 synchronized 關鍵字。 

        在一個類中,用關鍵字 synchonized 聲明的方法爲同步方法。Java 有一個專門負責管理線程對象中同步方法訪問的工具——同步模型監視器,它的原理是爲每個具有同步代碼的對象準備惟一的一把。當多個線程訪問對象時,只有取得鎖的線程才能進入同步方法,其他訪問共享對象的線程停留在對象中等待,如果獲得鎖的線程調用wait方法放棄鎖,那麼其他等待獲得鎖的線程將有機會獲得鎖。當某一個等待線程取得鎖,它將執行同步方法,而其他沒有取得鎖的線程仍然繼續等待獲得鎖。 
       Java
程序中線程之間通過消息實現相互通信,wait()notify() notifyAll()方法可完成線程間的消息傳遞。例如,一個對象包含一個 synchonized 同步方法,同一時刻只能有一個獲得鎖的線程訪問該對象中的同步方法,其他線程被阻塞在對象中等待獲得鎖。當線程調用 wait()方法可使該線程進入阻塞狀態,其他線程調用notify() notifyAll()方法可以喚醒該線程。 

2 .同步格式 

    當把一語句塊聲明爲 synchornized,在同一時間,它的訪問線程之一才能執行該語句塊。

 

  

 //方法同步:用關鍵字 synchonized 可將方法聲明爲同步	
class 類名{   
	     public synchonized 類型名稱 方法名稱(){   
	           ......   
	     }   
	}   
 // 語句塊同步: 對於同步塊,synchornized 獲取的是參數中的對象鎖。 
	synchornized(obj)   
	{    
	  //………………….    
	}   

   當線程執行到這裏的同步塊時,它必須獲取 obj 這個對象的鎖才能執行同步塊;否則線程只能等待獲得鎖。必須注意的是obj對象的作用範圍不同,控制情況不盡相同。示例如下。  

	public void method()   
	{    
	  Object obj= new Object(); //創建局部Object類型對象obj   
	  synchornized(obj)   //同步塊   
	  {   
	      //……………..    
	  }    
	}   

    上面的代碼創建了一個局部對象obj。由於每一個線程執行到 Object obj = new Object()時都會產生一個 obj 對象,每一個線程都可以獲得創建的新的 obj對象的鎖,不會相互影響,因此這段程序不會起到同步作用。

     3)同步類的屬性:如果同步的是類的屬性,情況就不同了。同步類的成員變量的一般格式如下。 

class method   
{    
    Object o = new Object();  //創建Object類型的成員變量o   
public void test()   
{    
synchornized(o)  //同步塊   
{    
            //………………………   
        }    
	    }    
	}   

  當兩個併發線程訪問同一個對象的 synchornized(o)同步代碼塊時,一段時間內只能有一個線程運行。另外的線程必須等到當前線程執行完同步代碼塊釋放鎖之後,獲得鎖的線程將執行同步代碼塊。

     有時可以通過下面的格式聲明同步塊。 

    public void method()   
    {    
    synchornized(this)  //同步塊   
    {    
        //………………………   
        }    
    }   

  當有一個線程訪問某個對象的 synchornized(this)同步代碼塊時,另外一個線程必須等待該線程執行完此代碼塊,其他線程可以訪問該對象中的非 synchornized(this)同步代碼。如果類中包含多個 synchornized(this)同步代碼塊,如果同步線程有一個訪問其中一個代碼塊,則其他線程不能訪問該對象的所有 synchornized(this)同步代碼塊。對於下面形式的同步塊而言,調用 ClassName 對象實例的並行線程中只有一個線程能夠訪問該對象。 

	synchornized(ClassName.class)   
	{    
	    //…………………….   
	}   

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章