ibe*_*rck 46 java multithreading deadlock
我正在研究java线程和死锁,我理解死锁的例子,但我想知道是否有一般规则要遵循以防止它.
我的问题是,是否有规则或技巧可以应用于java中的源代码以防止死锁?如果是,您能解释一下如何实施吗?
JB *_*zet 35
我的一些快速提示
Des*_*tar 15
封装,封装,封装!你可以用锁做出的最危险的错误可能是把你的锁暴露给世界(让它公开).没有人知道如果你这样做会发生什么,因为任何人都可以在没有对象知道的情况下获得锁定(这也是你不应该锁定的原因this).如果您将锁保密,那么您可以完全控制,这使其更易于管理.
Zim*_*oot 12
ConcurrentLinkedQueue而不是同步ArrayList)dje*_*lin 10
阅读并理解Java:并发和实践. 这不是关于避免死锁的"提示".我永远不会雇用一个知道一些技巧的开发人员来避免死锁并且经常避免死锁.这是关于理解并发性的.幸运的是,有一本关于该主题的综合中级水平书,所以请阅读它.
我喜欢这个例子。它启动两个共享布尔标志的线程:
public class UntilYouUpdateIt
{
public static boolean flag = true;
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(()->
{
while(flag){}
System.out.println("end");
});
t1.start();
Thread.sleep(100);
Thread t2 = new Thread(()->
{
flag = false;
System.out.println("changed");
});
t2.start();
}
}
Run Code Online (Sandbox Code Playgroud)
第一个线程将循环直到flag为 false,这发生在第二个线程的第一行。该程序永远不会完成,它的输出将是:
changed
Run Code Online (Sandbox Code Playgroud)
第二个线程死亡,同时第一个线程将永远循环。
为什么会发生这种情况?Compiler opmitizations。Thread1 永远不会再次检查标志的值,如下所示:
换句话说,Thread1 将始终flag从 中读取值cache,该值被设置为true。
解决/测试此问题的两种方法:
Thread t1 = new Thread(()->
{
while(flag)
{
System.out.print("I'm loopinnnng");
}
System.out.println("end");
});
Run Code Online (Sandbox Code Playgroud)
如果包含一些“重”操作(int i=1或类似的操作也不起作用),例如调用System,优化器会更加小心,检查flag布尔值以了解他是否没有浪费资源。输出将是:
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
(....)
changed
end
Run Code Online (Sandbox Code Playgroud)
或者
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
I'm loopinnnng
(....)
end
changed
Run Code Online (Sandbox Code Playgroud)
取决于最后分配给哪个线程 cpu 时间。
在使用布尔变量时,避免此类死锁的正确解决方案应该包括关键字volatile。
volatile告诉编译器:当涉及到这个变量时不要尝试优化。
因此,同样的代码只添加了该关键字:
public class UntilYouUpdateIt
{
public static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException
{
Thread t1 = new Thread(()->
{
while(flag){}
System.out.println("end");
});
t1.start();
Thread.sleep(100);
Thread t2 = new Thread(()->
{
flag = false;
System.out.println("changed");
});
t2.start();
}
}
Run Code Online (Sandbox Code Playgroud)
将输出:
changed
end
Run Code Online (Sandbox Code Playgroud)
或者
end
changed
Run Code Online (Sandbox Code Playgroud)
结果是两个线程都正确完成,避免了任何死锁。
这是一个基本的:
public void methodA()
{
//...
synchronized(lockA)
{
//...
synchronized(lockB)
{
//...
}
}
}
public void methodB()
{
//...
synchronized(lockB)
{
//...
synchronized(lockA)
{
//...
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果被许多线程调用,此方法可能会造成严重的死锁。这是因为对象以不同的顺序锁定。这是死锁最常见的原因之一,因此如果您想避免死锁,请确保按顺序获取锁。
在防止死锁方面,几乎只有一个大规则:
如果您的代码中需要有多个锁,请确保每个人始终以相同的顺序获取它们。
不过,让您的代码免于锁定应该几乎始终是您的目标。您可以尝试通过使用不可变或线程本地对象和无锁数据结构来摆脱它们。