防止java中死锁的提示

ibe*_*rck 46 java multithreading deadlock

我正在研究java线程和死锁,我理解死锁的例子,但我想知道是否有一般规则要遵循以防止它.

我的问题是,是否有规则或技巧可以应用于java中的源代码以防止死锁?如果是,您能解释一下如何实施吗?

JB *_*zet 35

我的一些快速提示

  • 不要使用多个线程(比如Swing,例如,强制要求所有内容都在EDT中完成)
  • 不要一次握住几把锁.如果这样做,请始终以相同的顺序获取锁
  • 持有锁时不要执行外部代码
  • 使用可中断的锁

  • 你能用*"外国"*代码澄清你的意思吗?进行网络通话的代码?呼叫阻止?打电话到你不写的其他课程?调用在其他国家/地区编写的代码:) ... (3认同)

Des*_*tar 15

封装,封装,封装!你可以用锁做出的最危险的错误可能是把你的锁暴露给世界(让它公开).没有人知道如果你这样做会发生什么,因为任何人都可以在没有对象知道的情况下获得锁定(这也是你不应该锁定的原因this).如果您将锁保密,那么您可以完全控制,这使其更易于管理.


Zim*_*oot 12

  1. 通过使用无锁数据结构来避免锁定(例如,使用ConcurrentLinkedQueue而不是同步ArrayList)
  2. 始终以相同的顺序获取锁,例如为每个锁分配唯一的数值并获取具有较低数值的锁,然后获取具有较高数值的锁
  3. 在超时期限后释放锁(从技术上讲,这不会阻止死锁,只是在它们发生后才有助于解决它们)

  • 无锁数据结构不会导致死锁.[ConcurrentLinkedQueue](http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html)是一个无锁数据结构的例子 - 你可以`offer`和`poll`来自多个线程的队列,无需同步对它的访问 (2认同)

dje*_*lin 10

阅读并理解Java:并发和实践. 这不是关于避免死锁的"提示".我永远不会雇用一个知道一些技巧的开发人员来避免死锁并且经常避免死锁.这是关于理解并发性的.幸运的是,有一本关于该主题的综合中级水平书,所以请阅读它.


jon*_*ejj 8

  1. 不要使用锁.
  2. 如果必须,请将锁保持在本地.全局锁定可能非常棘手.
  3. 握住锁时尽量少做.
  4. 使用条纹仅锁定数据段
  5. 首选不可变类型.很多时候,这意味着复制数据而不是共享数据.
  6. 使用比较和设置(CAS)机制,例如,参见AtomicReference.


ara*_*ran 6

布尔标志示例

我喜欢这个例子。它启动两个共享布尔标志的线程:

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 永远不会再次检查标志的值,如下所示:

  • 循环内的操作很便宜(无事可做)
  • 编译器知道没有其他实体可以修改标志值(因为第一个线程不能,而第二个线程已经死了)。所以它假设 flag 永远是 true

换句话说,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)

如果被许多线程调用,此方法可能会造成严重的死锁。这是因为对象以不同的顺序锁定。这是死锁最常见的原因之一,因此如果您想避免死锁,请确保按顺序获取锁。


Kep*_*pil 5

在防止死锁方面,几乎只有一个大规则:

如果您的代码中需要有多个锁,请确保每个人始终以相同的顺序获取它们。

不过,让您的代码免于锁定应该几乎始终是您的目标。您可以尝试通过使用不可变或线程本地对象和无锁数据结构来摆脱它们。


Mar*_*mes 5

给定设计选择,使用消息传递,其中只有锁定在队列push/pop中.这并不总是可行,但如果是这样,你将会遇到很少的死锁.你仍然可以得到它们,但你必须努力尝试:)