- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在看《七周内的七个并发模型》一书,在第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/
我是一名优秀的程序员,十分优秀!