Java 调用两个同步方法时发生死锁 类下载程序扩展线程{ 私有输入流; 私有输出流输出; 私有ArrayList侦听器; 公共下载程序(URL、字符串outputFilename)引发IOException{ in=url.openConnection().getInputStream(); out=新文件OutputStream(outputFilename); 侦听器=新的ArrayList(); } 公共同步的void addListener(ProgressListener侦听器){ 添加(侦听器); } 公共同步的void RemovelListener(ProgressListener侦听器){ 删除(侦听器); } 私有同步的void updateProgress(int n){ for(ProgressListener:侦听器) onProgress(n); } 公开募捐{ int n=0,总计=0; 字节[]缓冲区=新字节[1024]; 试一试{ 而((n=in.read(buffer))!=-1){ out.write(缓冲区,0,n); 总+=n; updateProgress(总计); } out.flush(); }捕获(IOE){} } }
以上代码摘自《七周七个并发模型》一书。这本书说,上面的代码有可能出现死锁,因为同步方法updateProgress调用可能获得另一个锁的外来方法[onProgress]。 由于我们在没有正确顺序的情况下获取了两个锁,因此可能会发生死锁 有人能解释一下在上述场景中死锁是如何发生的吗Java 调用两个同步方法时发生死锁 类下载程序扩展线程{ 私有输入流; 私有输出流输出; 私有ArrayList侦听器; 公共下载程序(URL、字符串outputFilename)引发IOException{ in=url.openConnection().getInputStream(); out=新文件OutputStream(outputFilename); 侦听器=新的ArrayList(); } 公共同步的void addListener(ProgressListener侦听器){ 添加(侦听器); } 公共同步的void RemovelListener(ProgressListener侦听器){ 删除(侦听器); } 私有同步的void updateProgress(int n){ for(ProgressListener:侦听器) onProgress(n); } 公开募捐{ int n=0,总计=0; 字节[]缓冲区=新字节[1024]; 试一试{ 而((n=in.read(buffer))!=-1){ out.write(缓冲区,0,n); 总+=n; updateProgress(总计); } out.flush(); }捕获(IOE){} } },java,multithreading,concurrency,java.util.concurrent,dining-philosopher,Java,Multithreading,Concurrency,Java.util.concurrent,Dining Philosopher,以上代码摘自《七周七个并发模型》一书。这本书说,上面的代码有可能出现死锁,因为同步方法updateProgress调用可能获得另一个锁的外来方法[onProgress]。 由于我们在没有正确顺序的情况下获取了两个锁,因此可能会发生死锁 有人能解释一下在上述场景中死锁是如何发生的吗 提前感谢。以下示例导致死锁,因为MyProgressListener试图获取下载程序锁,而它已经被获取 class Downloader extends Thread { private InputStream
提前感谢。以下示例导致死锁,因为MyProgressListener试图获取下载程序锁,而它已经被获取
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) { }
}
}
下面的示例导致死锁,因为MyProgressListener试图获取下载程序锁,而它已经被获取
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) { }
}
}
可能是这段代码中的问题:
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();
当一个持有锁的线程调用外部方法时
像这个(onProgress),你不能保证
此方法的实现不会尝试获取其他锁,
可以用不同的线来固定。这可能会导致死锁。可能是此代码中的问题:
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();
当一个持有锁的线程调用外部方法时
像这个(onProgress),你不能保证
此方法的实现不会尝试获取其他锁,
可以用不同的线来固定。这可能会导致死锁。首先,回想一下,将关键字
synchronized
应用于类时,意味着锁定此方法所属的整个对象。现在,让我们画出另两个触发死锁的对象:
for (ProgressListener listener: listeners)
listener.onProgress(n);
class DLlistener implements ProgressListener {
private Downloader d;
public DLlistener(Downloader d){
this.d = d;
// here we innocently register ourself to the downloader: this method is synchronized
d.addListener(this);
}
public void onProgress(int n){
// this method is invoked from a synchronized call in Downloader
// all we have to do to create a dead lock is to call another synchronized method of that same object from a different thread *while holding the lock*
DLthread thread = new DLThread(d);
thread.start();
thread.join();
}
}
// this is the other thread which will produce the deadlock
class DLThread extends Thread {
Downloader locked;
DLThread(Downloader d){
locked = d;
}
public void run(){
// here we create a new listener, which will register itself and generate the dead lock
DLlistener listener(locked);
// ...
}
}
避免死锁的一种方法是通过让内部侦听器队列等待添加/删除来推迟在
addListener
中完成的工作,并让Downloader
自己定期处理这些侦听器。这最终取决于下载程序。当然要运行内部工作。首先,回想一下关键字synchronized
,当应用于类时,意味着锁定此方法所属的整个对象。现在,让我们画出另两个触发死锁的对象:
for (ProgressListener listener: listeners)
listener.onProgress(n);
class DLlistener implements ProgressListener {
private Downloader d;
public DLlistener(Downloader d){
this.d = d;
// here we innocently register ourself to the downloader: this method is synchronized
d.addListener(this);
}
public void onProgress(int n){
// this method is invoked from a synchronized call in Downloader
// all we have to do to create a dead lock is to call another synchronized method of that same object from a different thread *while holding the lock*
DLthread thread = new DLThread(d);
thread.start();
thread.join();
}
}
// this is the other thread which will produce the deadlock
class DLThread extends Thread {
Downloader locked;
DLThread(Downloader d){
locked = d;
}
public void run(){
// here we create a new listener, which will register itself and generate the dead lock
DLlistener listener(locked);
// ...
}
}
避免死锁的一种方法是通过让内部侦听器队列等待添加/删除来推迟在
addListener
中完成的工作,并让Downloader
自己定期处理这些侦听器。这最终取决于下载程序。当然,运行内部工作。最好将与同步的对象设置为私有对象
由于您在下载程序
上同步,因此不知道其他线程是否也在下载程序
上同步
以下侦听器导致死锁:
for (ProgressListener listener: listeners)
listener.onProgress(n);
class DLlistener implements ProgressListener {
private Downloader d;
public DLlistener(Downloader d){
this.d = d;
// here we innocently register ourself to the downloader: this method is synchronized
d.addListener(this);
}
public void onProgress(int n){
// this method is invoked from a synchronized call in Downloader
// all we have to do to create a dead lock is to call another synchronized method of that same object from a different thread *while holding the lock*
DLthread thread = new DLThread(d);
thread.start();
thread.join();
}
}
// this is the other thread which will produce the deadlock
class DLThread extends Thread {
Downloader locked;
DLThread(Downloader d){
locked = d;
}
public void run(){
// here we create a new listener, which will register itself and generate the dead lock
DLlistener listener(locked);
// ...
}
}
导致死锁的代码:
如果运行该代码,将发生以下情况:
主线程到达updateProgress
并在下载程序上获得锁
调用MyProgressListener
的onProgress
方法,并启动新线程t
主线程到达t.join()代码>
在这种情况下,主线程在t
完成之前无法继续,但要完成t
,主线程必须释放其在下载程序上的锁,但这不会发生,因为主线程无法处理->死锁最好将与同步的对象设置为私有对象
由于您在下载程序
上同步,因此不知道其他线程是否也在下载程序
上同步
以下侦听器导致死锁:
for (ProgressListener listener: listeners)
listener.onProgress(n);
class DLlistener implements ProgressListener {
private Downloader d;
public DLlistener(Downloader d){
this.d = d;
// here we innocently register ourself to the downloader: this method is synchronized
d.addListener(this);
}
public void onProgress(int n){
// this method is invoked from a synchronized call in Downloader
// all we have to do to create a dead lock is to call another synchronized method of that same object from a different thread *while holding the lock*
DLthread thread = new DLThread(d);
thread.start();
thread.join();
}
}
// this is the other thread which will produce the deadlock
class DLThread extends Thread {
Downloader locked;
DLThread(Downloader d){
locked = d;
}
public void run(){
// here we create a new listener, which will register itself and generate the dead lock
DLlistener listener(locked);
// ...
}
}
导致死锁的代码:
如果运行该代码,将发生以下情况:
主线程到达updateProgress
并在下载程序上获得锁
调用MyProgressListener
的onProgress
方法,并启动新线程t
主线程到达t.join()代码>
在这种情况下,主线程在t
完成之前无法继续,但要完成t
,主线程必须释放其在下载程序上的锁,但这不会发生,因为主线程无法处理->死锁下面是一个经典示例,它显示了作者试图避免的那种难以调试的问题
创建类UseDownloader
,并调用downloadSomething
随着下载的进行,调用onProgress
方法。由于这是从同步块内调用的,下载程序
motinor被锁定。在我们的onProgress
方法中,我们需要锁定自己的资源,在本例中是lock
。因此,当我们尝试在lock