2012-10-26 98 views
-1

此代碼有時甚至在我使用​​方法removeFirst內​​塊運行方法,我添加和刪除synchronizedList上的元素。爲什麼Synchronization在下面的代碼中不起作用?

public class NameDropper extends Thread { 

    private NameList n1; 

    public NameDropper(List list) { 
     this.n1 = new NameList(list); 
    } 

    public static void main(String[] args) { 
     List l = Collections.synchronizedList(new LinkedList()); 
     NameDropper n = new NameDropper(l); 
     n.n1.add("Ozymandias"); 
     Thread[] t = new NameDropper[10]; 
     for (int i = 1; i <= 10; i++) { 
      t[i - 1] = new NameDropper(l); 
      t[i - 1].setName("T" + Integer.toString(i - 1)); 
      t[i - 1].start(); 
     } 
    } 

    public void run() { 
     synchronized (this) { 
      try { 
       Thread.sleep(50); 
       String name = n1.removeFirst(); 
       System.out.println(Thread.currentThread().getName() + ": " 
        + name); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

class NameList { 
    private List names = null; 

    public NameList(List list) { 
     this.names = list; 
    } 

    public synchronized void add(String name) { 
     names.add(name); 
    } 

    public synchronized String removeFirst() { 
     if (names.size() > 0) 
      return (String) names.remove(0); 
     else 
      return null; 
     } 
} 

唯一的例外是拋出:

T1: Ozymandias  
T2: null  
*Exception in thread "T3" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0  
    at java.util.LinkedList.entry(Unknown Source) 
    at java.util.LinkedList.remove(Unknown Source) 
    at java.util.Collections$SynchronizedList.remove(Unknown Source) 
    at NameList.removeFirst(NameDropper.java:57)* 
T0: null  
T8: null  
*at NameDropper.run(NameDropper.java:33)*  
T6: null  
T4: null  
T9: null  
T7: null  
T5: null  
+1

您應該使用泛型 – SLaks

+0

如果您在同步方法中執行所有列表訪問,則不需要synchronizedList。您不會冒任何併發訪問結構的風險,因此添加的同步只會減慢您的代碼。 –

回答

2

正如其他人所指出的那樣,您有競爭狀態,因爲您的所有線程都是自己同步的。你需要一個共同的對象來同步。

我會建議你在列表本身上同步。這意味着任何競爭同一個列表的實例都會彼此阻塞,並且任何不是的線程都不會被阻塞。你的添加和刪除方法應該是:

public void add(String name) { 
    synchronized (name) { 
     names.add(name); 
    } 
} 

public String removeFirst() { 
    synchronized (name) { 
     if (names.size() > 0) 
      return (String) names.remove(0); 
     else 
      return null; 
    } 
} 
5

您正在爲每個線程新NameDropper實例。
因此,​​方法實際上並沒有鎖定,因爲每個實例都不會被兩個線程使用。

+0

對。該建議總是要在引用永不改變的'final'對象上同步。 – Gray

1

即使您使用的是Collections.synchronizedList代碼中存在競爭條件。

下面是你的代碼中的種族編程的例子。

lock(NameDropper[0])       lock(NameDropper[1]) 
names.size() > 0 is true      names.size() > 0 is true 
               names.remove(0) 
names.remove(0) <--- Error here. 

因爲要創建NameDropper實例爲每個線程which shares single instance of List你有這樣的競爭條件。

你可以做的是對每個NameDropper

 List l1 = Collections.synchronizedList(new LinkedList()); 
     t[i - 1] = new NameDropper(l1); 

這樣每個NameDropper將有它自己的List實例創建單獨的列表。

1

一般情況下:
1)由於每次創建類的新實例時,基本上沒有用於鎖定所有線程的「通用」對象。您應該定義類似於:

static final Object lock = new Object(); 

synchronize而不是此對象。

2)恕我直言,最好是執行Runnable而不是延長Thread

0

正如其他人所說的,NameList不被共享。這裏有一個最小的重新編碼方式來修復您的代碼(還有其他):

更改構造函數以接受NameList(不是List)。

public NameDropper(NameList list) { 
    this.n1 = list; 
} 

創建您正在創建列表的NameList。

NameList l = new NameList(Collections.synchronizedList(new LinkedList())); 
相關問題