JAVA多線程編程陷阱總結二

來源:互聯網
上載者:User

陷阱7:正確使用靜態同步方法

Java語言規定:任何線程進入同步方法,同步代碼塊之前,必須先擷取同步方法,同步代碼塊對應的同步監視器。對於同步代碼塊而言,程式必須顯示為它指定同步監視器;對於同步非靜態方法而言,該方法的同步監視器是this-即調用該方法的Java對象;對於靜態同步方法而言,該方法的同步監視器不是this,而是該類本身。

class SynchronizedStatic implements Runnable {
static boolean flag = true;
public static synchronized void test0() {//同步監視器是該類本身
for (int i = 0; i < 1000; i++) {
System.out.println("test0: " + Thread.currentThread().getName()
+ " " + i);
}
}
public void test1() {
synchronized (this) {//同步監視器是this,即調用該方法的Java對象。
for (int i = 0; i < 1000; i++) {
System.out.println("test1: " + Thread.currentThread().getName()
+ " " + i);
}
}
}
public void run() {
if (flag) {
flag = false;
test0();
} else {
flag = true;
test1();
}
}
public static void main(String args[]) throws InterruptedException {
SynchronizedStatic ss = new SynchronizedStatic();
new Thread(ss).start();
Thread.sleep(1);
new Thread(ss).start();
}
}

運行結果:
test0: Thread-0 244
test1: Thread-1 7
test0: Thread-0 245
test1: Thread-1 8
test0: Thread-0 246
test1: Thread-1 9
test0: Thread-0 247
test1: Thread-1 10
test1: Thread-1 11
test1: Thread-1 12
test0: Thread-0 248
test1: Thread-1 13
test0: Thread-0 249
test0: Thread-0 250


從運行結果可以看出:靜態同步方法可以和以this為同步監視器的同步代碼塊同時執行。因為兩者的同步監視器不一樣,前者是對SynchronizedStatic類的鎖定,後者是對ss變數所引用的對象的鎖定,因此程式可以在兩個線程間相互切換。若將test1()方法做以下更改:

public void test1() 

{

synchronized (<SPAN style="COLOR: #ff0000">SynchronizedStatic.class</SPAN>)

 {//和靜態方法具有了相同的同步監視器
for (int i = 0; i < 1000; i++) 

{

System.out.println("test1: " + Thread.currentThread().getName() + " " + i);

}
}
}

運行結果:

test0: Thread-0 995
test0: Thread-0 996
test0: Thread-0 997
test0: Thread-0 998
test0: Thread-0 999
test1: Thread-1 0
test1: Thread-1 1
test1: Thread-1 2
test1: Thread-1 3
test1: Thread-1 4

結果說明:靜態同步方法和以當前類為同步監視器的同步代碼塊不能同時執行。

陷阱8:不要同步boolean常量

在JAVA中通過synchronized語句可以實現多線程並發。使用同步代碼塊,JVM保證同一時間只有一個線程可以擁有某一對象的鎖。鎖機制實現了多個安全執行緒地對臨界資源進行訪問。
同步代碼寫法如下:

Object obj = new Object();
...
synchronized(obj) {
  //TODO: 訪問臨界資源
}

JAVA的多線程總是充滿陷阱,如果我們用Boolean作為被同步的對象,可能會出現以下兩種情況:

一. 以為對一個對象加鎖,實際同步的是不同對象。

private volatile Boolean isTrue = false;
publich void aMethod() {
  ...
  synchronized(isTrue) {
    isTrue = !isTrue;
    //TODO: 訪問臨界資源
    isTrue = !isTrue;
  }
  ...
}

咋一看上面的代碼沒有問題,由於使用了synchronized(isTrue)同一時間只能有一個線程訪問臨界資源,但事實並不是這樣。因為false和true這兩個常量對應著兩個不同的對象。當isTrue產生變化時,很可能導致不同的線程同步了不同的對象。JAVA的自動裝箱會將false變為Boolean.FALSE,將true變為Boolean.TRUE(同時這也說明了此處若將false改為Boolean.FALSE其結果也是一樣的)。寫一個以上情況的測試代碼如下:

public class BooleanTest {
private volatile Boolean isTrue = Boolean.FALSE; //此處用false也一樣
public void aMethod() {
for(int i=0;i<10;i++) {
Thread t = new Thread() {
public void run() {
synchronized(isTrue) {
isTrue = !isTrue;
System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue);
try{
Double ran = 1000 * Math.random();
Thread.sleep(ran.intValue());
}catch(InterruptedException e) {}

if(!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!");

isTrue = !isTrue;
}
}
};
t.start();
}
}
public static void main(String... args) {
BooleanTest bt = new BooleanTest();
bt.aMethod();
}
}

運行以上代碼,不時的會看到 " - Oh, No!",表示不同的線程同時進入到synchronized代碼塊中。

二. 以為同步的是不同對象,實際是一個對象。

有時候我們可能希望在多個對象上進行同步,如果使用了Boolean作為被同步對象,很可能會導致本來應該沒有關係的兩個同步塊使用了相同對象的鎖。樣本如下:

private volatile Boolean aBoolean = Boolean.FALSE;
private volatile Boolean anotherBoolean = false;
public void aMethod() {
  ...
  synchronized(aBoolean) {
    //TODO: 訪問臨界資源1
  }
  ...
}
public void anotherMethod() {
  ...
  synchronized(anotherBoolean) {
    //TODO: 訪問臨界資源2
  }
  ...
}

假設原本aMethod和anotherMethod分別會被兩組沒有關係的線程調用。但是由於Boolean.FALSE和false指向的是同一個對象,可能導致對臨界資源2的訪問被臨界資源1阻塞了(反之亦然)。

以上兩種情況說明,在使用同步塊時,盡量不用使用Boolean對象作為被同步對象,不然可能會出現意想不到的問題,或者對以後的代碼修改造成陷阱。

從此也可以看出,任何對常量的同步都是有風險的。如果一定要對 Boolean 進行同步,一定要用 new 操作符來建立 Boolean 對象。

原文:http://johnshen0708.iteye.com/blog/1393620

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.