1
class Downloader extends Thread {
    private InputStream in;
    private OutputStream out;
    private ArrayList<ProgressListener> listeners;
    public Downloader(URL url, String outputFilename) throws IOException {
        in = url.openConnection().getInputStream();
        out = new FileOutputStream(outputFilename);
        listeners = new ArrayList<ProgressListener>();
    }
    public synchronized void addListener(ProgressListener listener) {
        listeners.add(listener);
    }
    public synchronized void removeListener(ProgressListener listener) {
        listeners.remove(listener);
    }

    private synchronized void updateProgress(int n) {
        for (ProgressListener listener: listeners)
            listener.onProgress(n);
    }
    public void run() {
        int n = 0, total = 0;
        byte[] buffer = new byte[1024];
        try {
            while((n = in.read(buffer)) != -1) {
                out.write(buffer, 0, n);
                total += n;
                updateProgress(total);
            }
            out.flush();
        } catch (IOException e) { }
    }
}

上記のコードは、本「7 週間で 7 つの同時実行モデル」からのものです。この本によると、同期されたメソッド updateProgress が別のロックを取得する可能性のあるエイリアン メソッド [onProgress] を呼び出すため、上記のコードにはデッドロックが発生する可能性があります。正しい順序なしに 2 つのロックを取得するため、デッドロックが発生する可能性があります。

上記のシナリオでデッドロックがどのように発生するかを説明できる人はいますか?

前もって感謝します。

4

5 に答える 5

0

これは、作成者が回避しようとしているデバッグが困難な問題の種類を示す典型的な例です。

クラスUseDownloaderが作成され、downloadSomething呼び出されます。

ダウンロードが進行するにつれて、onProgressメソッドが呼び出されます。これは同期ブロック内から呼び出されるため、Downloaderモーターがロックされます。メソッド内onProgressで、独自のリソース (この場合は ) をロックする必要がありますlock。したがって、同期しようとしているときは、モニターlockを保持しています。Downloader

別のスレッドがダウンロードをキャンセルする必要があると判断した場合は、 を呼び出しますsetCanceled。これは最初にテストdoneしてモニター上で同期しlock、次に を呼び出しますremoveListener。ただし、ロックremoveListenerが必要です。Downloader

この種のデッドロックは、あまり頻繁に発生しないため、見つけるのが難しい場合があります。

  public static final int END_DOWNLOAD = 100;

  class UseDownloader implements ProgressListener {
    Downloader d;
    Object lock = new Object();
    boolean done = false;

    public UseDownloader(Downloader d) {
      this.d = d;
    }
    public void onProgress(int n) {
      synchronized(lock) {
        if (!done) {
          // show some progress
        }
      }
    }

    public void downloadSomething() {
      d.addListener(this);
      d.start();
    }

    public boolean setCanceled() {
      synchronized(lock) {
        if (!done) {
          done = true;
          d.removeListener(this);
        }
      }
    }
  }
于 2014-07-31T21:17:40.377 に答える
-1

次の例では、MyProgressListener が既に取得されている Downloader ロックを取得しようとするため、デッドロックが発生します。

class MyProgressListener extends ProgressListener {
    private Downloader myDownloader;

    public MyProgressListener(Downloader downloader) {
        myDownloader = downloader;
    }

    public void onProgress(int n) {
        // starts and waits for a thread that accesses myDownloader
    }
}

Downloader downloader = new Downloader(...);
downloader.addListener(new MyListener(downloader));
downloader.run();
于 2014-07-31T04:12:36.047 に答える