java線程同步

from url : http://blog.csdn.net/weizhaozhe/article/details/3922647


同步(阻塞) :是一種防止對共享資源訪問導致的數據不一致的一種模式。

詳細請參看操作系統。

在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()通知的是等待隊列中的所有線程

例子程序:

/**

 *PrintNum.java

 * Created on 5:18:04 PM Feb 22, 2009

 *@author Quasar063501

 *@version 0.1

 * 

 */

public class PrintNum {

private byte[] lock = new byte[0];  //自定義鎖對象,這樣代價最小,也可已使用當前對象this

public void demo() {

PrintThread a = new PrintThread("a");

PrintThread b = new PrintThread("b");

a.start();

b.start();

}

class PrintThread extends Thread {

public PrintThread(String name) {

this.setName(name);

}

public void run() {

synchronized(lock) {

for(int i =0; i < 100; i++) {

if(i % 10 == 0 && 0 != i) {

try {

lock.wait();   //暫時釋放資源

lock.notify();       //喚醒另外一個進程

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(this.getName()+": "+i);

}

}

}

}

}

這個程序最終會因爲互相喚醒而死鎖,請你解決

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