-1

このコードは、実行メソッドのブロック内でsynchronizedメソッドを使用した場合でも例外をスローすることがあり ます。removeFirstsynchronizedsynchronizedList

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    
4

5 に答える 5

5

NameDropperスレッドごとに新しいインスタンスを作成しています。
したがって、synchronized各インスタンスが 2 つのスレッドによって使用されることはないため、メソッドは実際にはロックされません。

于 2012-10-26T13:54:02.760 に答える
2

他の人が指摘したように、すべてのスレッドが同期されているため、競合状態が発生しています。同期する共通オブジェクトが必要です。

リスト自体で同期することをお勧めします。これは、同じリストで競合しているインスタンスが互いにブロックされ、競合していないスレッドはブロックされないことを意味します。add メソッドと remove メソッドは次のようになります。

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;
    }
}
于 2012-10-26T14:25:26.140 に答える
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

于 2012-10-26T14:03:08.040 に答える
1

一般に:
1) 毎回クラスの新しいインスタンスを作成しているため、基本的に、すべてのスレッドがロックする「共通の」オブジェクトはありません。次のように定義する必要があります。

static final Object lock = new Object();

synchronize代わりにこのオブジェクトで。

2)私見でRunnableは、拡張するよりも実装する方が望ましいThreadです。

于 2012-10-26T14:04:42.057 に答える
0

他の人が述べているように、NameList は共有されていません。コードを修正するための最小限の再コーディングを行う 1 つの方法を次に示します (他にもあります)。

(List ではなく) NameList を受け取るようにコンストラクターを変更します。

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

現在リストを作成している場所に NameList を作成します。

NameList l = new NameList(Collections.synchronizedList(new LinkedList()));
于 2012-10-26T16:27:34.417 に答える