gpt4 book ai didi

java - java并发中的alien方法难懂

转载 作者:行者123 更新时间:2023-11-29 04:08:41 25 4
gpt4 key购买 nike

我正在看《七周内的七个并发模型》一书,在第2章中有关于alien方法的描述。

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)
{
}
}
}

Because addListener(), removeListener(), and updateProgress() are all synchronized, multiple threads can call them without stepping on one another’s toes. But a trap lurks in this code that could lead to deadlock even though there’s only a single lock in use. The problem is that updateProgress() calls an alien method—a method it knows nothing about. That method could do anything, including acquiring another lock. If it does, then we’ve acquired two locks without knowing whether we’ve done so in the right order. As we’ve just seen, that can lead to deadlock. The only solution is to avoid calling alien methods while holding a lock. One way to achieve this is to make a defensive copy of listeners before iterating through it:

private void updateProgress(int n) { 
ArrayList<ProgressListener> listenersCopy;
synchronized(this) {
listenersCopy = (ArrayList<ProgressListener>)listeners.clone();
}
for (ProgressListener listener: listenersCopy)
listener.onProgress(n);
}

看了这个解释,我还是不明白onProgress方法怎么会造成死锁,为什么clone监听列表可以避免这个问题。

最佳答案

I still can't understand how the onProgress method can cause dead lock

假设我们有一个 ProgressListener 实现

Downloader  downloader = new Downloader();

downloader.addListener(new ProgressListener(){
public void onProgress(int n) {
// do something
Thread th = new Thread(() -> downloader.addListener(() -> {});
th.start();
th.join();
}
});

downloader.updateProgress(10);

第一次调用 addListener 会成功。但是,当您调用 updateProgress 时,将触发 onProgress 方法。当 onProgress 被触发时,它永远不会完成,因为正在调用 addListener 方法(在 sync 方法上阻塞),而 onProgress 仍在获取锁。这会导致死锁。

现在,这个示例是一个愚蠢的示例,因为实际上不会尝试创建死锁,但是复杂的代码路径和共享逻辑很容易导致某种形式的死锁。这里的重点是永远不要让自己处于那个位置

why clone the listener list can avoid the problem.

您希望同步访问集合,因为它由多个线程共享和改变。通过创建克隆,您不再允许其他线程改变您的集合(线程本地克隆)并且可以安全地遍历。

您仍然在读取时同步克隆,但 onProgress 的执行发生在 同步 之外。当您这样做时,我列出的示例将永远不会死锁,因为只有一个线程将获取 Downloaded 监视器。

关于java - java并发中的alien方法难懂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56529352/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com