天天看點

JAVA線程同步(一)

同步(阻塞) :是一種防止對共享資源通路導緻的資料不一緻的一種模式。

詳細請參看作業系統。

在java中,由于對多線程的支援,對同步的控制主要通過以下幾個方法,synchronized,和wait(),notify()和notifyall(),下面進行一一的講解:

a關鍵字synchronized

每個java對象都有一把鎖, 當有多個線程同時通路共享資源的時候, 需要synchronize 來控制安全性, synchronize 分 synchronize 方法 和synchronize塊,使用synchronize塊時, 一定要顯示的獲得該對象的鎖(如synchronize(object))而方法則不需要。

java的記憶體模型是對每一個程序有一個主記憶體, 每個線程有自己的記憶體, 他們從主記憶體中取資料, 然後計算, 再存入主記憶體中。 

并發問題如下:如果多個線程同僚操作同一資料, a線程從主記憶體中取的i的值為1, 然後進行加1操作, 這時b線程也取i的值, 進行加2操作, 然後a存入2到主記憶體中, b也存入, 這樣就覆寫了a的值(同資料庫中的并發問題一樣)。

解決辦法是用synchronize, 如用synchronized(i)。被synchronize 修飾的方法(塊)把以下三步操作當成一個原子操作:取資料, 操作資料, 存資料。 我們知道原子操作是不可以被打斷的, 是以其保證了資料一緻性, 這樣同一時間隻有一個線程再執行, 對性能有一定的影響。這也是synchronize的第二個作用:保證統一時間隻有一個線程再運作。 當實作socket連接配接的時候經常用到.

java中規定對非float, long的原始類型的取和存操作為原子操作。 其實就是對一個字(32位)的取,存位原始操作, 因為float, long為兩個位元組的長度, 是以其取, 存為非原子操作。 如果想把他們也變為原子操作, 可以用volatile關鍵字來修飾

使用方法:

作用區域主要有兩種:

1.方法

2.代碼塊

被synchronized聲明的方法被稱為同步方法,被其修飾的代碼塊稱為同步語句。無論是同步方法還是同步語句,隻要聲明為同步了,在同一時刻,同一個對象的同步xx是不可以被同時通路的,而不同對象之間的同步方法是互不幹擾的。

具體實作(如下代碼都在某個類定義中):

同步方法:

public synchronized void change() {

//

}

同步語句:(因為效率問題,有時考慮使用同步語句塊)

    public void change() {

synchronized(this) {

這個同步語句是針對目前對象的,有時,我們就是想讓一段代碼同步,可能與目前對象并沒什麼關系,可以自定義同步的鎖。如下:

private byte[]  lock= new byte[0];

  public void change() {

synchronized(lock) {

自定義鎖注意事項:

1必須是private,防止在類外部引用改變。

2如果可能用到,重寫get方法,傳回對象的clone,而不是本身。

其他用法:

synchronized除了可以作用于方法,代碼塊,還可以作用于靜态方法,類,某個執行個體。但是都存在效率問題,一定要慎用。

class foo   {   

public synchronizedstatic void methodaaa()// 同步的static 函數

{   

//….

}  

public void methodbbb()   {   

synchronized(foo.class)// class literal(類名稱字面常量)

 } }

這樣修飾後代表的是:統一時刻,被修飾部分隻有一個對象可以運作,因為它的聲明是針對類的。

2.wait()/notify()/notifyall()

注意:

在java中,每個對象都有個對象鎖标志(object lock flag)與之想關聯,當一個線程a調用對象的一段synchronized代碼時,

  它首先要擷取與這個對象關聯的對象鎖标志,然後執行相應的代碼,執行結束後,把這個對象鎖标志傳回給對象;是以,線上程a執行

  synchronized代碼期間,如果另一個線程b也要執行同一對象的一段synchronized代碼時(不一定與線程a執行的相同),它将

  要等到線程a執行完後,才能繼續....

  如何利用wait() notify() notifyall()?

  在synchronized代碼被執行期間,線程可以調用對象的wait()方法,釋放對象鎖标志,進入等待狀态,并且可以調用notify()或者

  notifyall()方法通知正在等待的其他線程。notify()通知等待隊列中的第一個線程,notifyall()通知的是等待隊列中的所有線程