Java多线程中的死锁

同步的 关键字用于使类或方法线程安全,这意味着只有一个线程可以拥有同步方法的锁并使用它,其他线程必须等待锁释放,并且其中任何线程都可以获得该锁。 如果我们的程序运行在两个或多个线程同时执行的多线程环境中,那么使用它是很重要的。但有时它也会引起一个问题,叫做 僵局 下面是死锁条件的一个简单示例。

null

图片[1]-Java多线程中的死锁-yiteyi-C++库

JAVA

// Java program to illustrate Deadlock
// in multithreading.
class Util
{
// Util class to sleep a thread
static void sleep( long millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
// This class is shared by both threads
class Shared
{
// first synchronized method
synchronized void test1(Shared s2)
{
System.out.println( "test1-begin" );
Util.sleep( 1000 );
// taking object lock of s2 enters
// into test2 method
s2.test2();
System.out.println( "test1-end" );
}
// second synchronized method
synchronized void test2()
{
System.out.println( "test2-begin" );
Util.sleep( 1000 );
// taking object lock of s1 enters
// into test1 method
System.out.println( "test2-end" );
}
}
class Thread1 extends Thread
{
private Shared s1;
private Shared s2;
// constructor to initialize fields
public Thread1(Shared s1, Shared s2)
{
this .s1 = s1;
this .s2 = s2;
}
// run method to start a thread
@Override
public void run()
{
// taking object lock of s1 enters
// into test1 method
s1.test1(s2);
}
}
class Thread2 extends Thread
{
private Shared s1;
private Shared s2;
// constructor to initialize fields
public Thread2(Shared s1, Shared s2)
{
this .s1 = s1;
this .s2 = s2;
}
// run method to start a thread
@Override
public void run()
{
// taking object lock of s2
// enters into test2 method
s2.test1(s1);
}
}
public class Deadlock
{
public static void main(String[] args)
{
// creating one object
Shared s1 = new Shared();
// creating second object
Shared s2 = new Shared();
// creating first thread and starting it
Thread1 t1 = new Thread1(s1, s2);
t1.start();
// creating second thread and starting it
Thread2 t2 = new Thread2(s1, s2);
t2.start();
// sleeping main thread
Util.sleep( 2000 );
}
}


Output : test1-begintest2-begin

不建议使用在线IDE运行上述程序。我们可以复制源代码并在本地机器上运行。我们可以看到它无限期地运行,因为线程处于死锁状态,不允许代码执行。现在让我们一步一步地看看那里发生了什么。

  1. 线程t1通过获取s1的对象锁来启动并调用test1方法。
  2. 线程t2通过获取s2的对象锁来启动并调用test1方法。
  3. t1打印test1 begin,t2打印test-2 begin,两个线程都等待1秒,这样两个线程中的任何一个都可以启动。
  4. t1试图获取s2的对象锁并调用方法test2,但由于t2已经获取了它,所以它会等待它释放。在得到s2锁之前,它不会释放s1锁。
  5. t2也是如此。它试图获取s1的对象锁并调用方法test1,但t1已经获取了它,所以它必须等到t1释放锁。t2也不会释放s2的锁,直到它获得s1的锁。
  6. 现在,两个线程都处于等待状态,等待对方释放锁。现在有一个竞争条件,谁将首先释放锁。
  7. 因为它们都没有准备好释放锁,所以这是死锁状态。
  8. 当你运行这个程序时,看起来执行暂停了。

检测死锁状态

我们也可以通过在cmd上运行这个程序来检测死锁。我们必须收集线程转储。要收集的命令取决于操作系统类型。如果我们使用的是Windows和Java 8,那么命令是jcmd$PID Thread。打印 我们可以通过运行jps命令获得PID。上述程序的线程转储如下:

jcmd 18692 Thread.print18692:2020-06-08 19:03:10Full thread dump OpenJDK 64-Bit Server VM (11.0.4+10-b304.69 mixed mode, sharing):Threads class SMR info:_java_thread_list=0x0000017f44b69f20, length=13, elements={0x0000017f43f77000, 0x0000017f43f79800, 0x0000017f43f90000, 0x0000017f43f91000,0x0000017f43f95000, 0x0000017f43fa5000, 0x0000017f43fb0800, 0x0000017f43f5b800,0x0000017f44bc9000, 0x0000017f44afb000, 0x0000017f44bd7800, 0x0000017f44bd8800,0x0000017f298c9000}"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=57.48s tid=0x0000017f43f77000 nid=0x6050 waiting on condition  [0x0000005f800ff000]   java.lang.Thread.State: RUNNABLE        at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.4/Native Method)        at java.lang.ref.Reference.processPendingReferences(java.base@11.0.4/Reference.java:241)        at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.4/Reference.java:213)"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=57.48s tid=0x0000017f43f79800 nid=0x2824 in Object.wait()  [0x0000005f801fe000]   java.lang.Thread.State: WAITING (on object monitor)        at java.lang.Object.wait(java.base@11.0.4/Native Method)        - waiting on  (a java.lang.ref.ReferenceQueue$Lock)        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.4/ReferenceQueue.java:155)        - waiting to re-lock in wait()  (a java.lang.ref.ReferenceQueue$Lock)        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.4/ReferenceQueue.java:176)        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.4/Finalizer.java:170)"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=57.47s tid=0x0000017f43f90000 nid=0x1710 runnable  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=31.25ms elapsed=57.47s tid=0x0000017f43f91000 nid=0x4ff4 waiting on condition  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 cpu=46.88ms elapsed=57.47s tid=0x0000017f43f95000 nid=0x350c waiting on condition  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE   No compile task"C1 CompilerThread0" #9 daemon prio=9 os_prio=2 cpu=93.75ms elapsed=57.47s tid=0x0000017f43fa5000 nid=0x4900 waiting on condition  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE   No compile task"Sweeper thread" #10 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=57.47s tid=0x0000017f43fb0800 nid=0x6120 runnable  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE"Common-Cleaner" #11 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=57.44s tid=0x0000017f43f5b800 nid=0x5a4 in Object.wait()  [0x0000005f807fe000]   java.lang.Thread.State: TIMED_WAITING (on object monitor)        at java.lang.Object.wait(java.base@11.0.4/Native Method)        - waiting on  (a java.lang.ref.ReferenceQueue$Lock)        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.4/ReferenceQueue.java:155)        - waiting to re-lock in wait()  (a java.lang.ref.ReferenceQueue$Lock)        at jdk.internal.ref.CleanerImpl.run(java.base@11.0.4/CleanerImpl.java:148)        at java.lang.Thread.run(java.base@11.0.4/Thread.java:834)        at jdk.internal.misc.InnocuousThread.run(java.base@11.0.4/InnocuousThread.java:134)"Monitor Ctrl-Break" #12 daemon prio=5 os_prio=0 cpu=15.63ms elapsed=57.36s tid=0x0000017f44bc9000 nid=0x5954 runnable  [0x0000005f809fe000]   java.lang.Thread.State: RUNNABLE        at java.net.SocketInputStream.socketRead0(java.base@11.0.4/Native Method)        at java.net.SocketInputStream.socketRead(java.base@11.0.4/SocketInputStream.java:115)        at java.net.SocketInputStream.read(java.base@11.0.4/SocketInputStream.java:168)        at java.net.SocketInputStream.read(java.base@11.0.4/SocketInputStream.java:140)        at sun.nio.cs.StreamDecoder.readBytes(java.base@11.0.4/StreamDecoder.java:284)        at sun.nio.cs.StreamDecoder.implRead(java.base@11.0.4/StreamDecoder.java:326)        at sun.nio.cs.StreamDecoder.read(java.base@11.0.4/StreamDecoder.java:178)        - locked  (a java.io.InputStreamReader)        at java.io.InputStreamReader.read(java.base@11.0.4/InputStreamReader.java:185)        at java.io.BufferedReader.fill(java.base@11.0.4/BufferedReader.java:161)        at java.io.BufferedReader.readLine(java.base@11.0.4/BufferedReader.java:326)        - locked  (a java.io.InputStreamReader)        at java.io.BufferedReader.readLine(java.base@11.0.4/BufferedReader.java:392)        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)"Service Thread" #13 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=57.36s tid=0x0000017f44afb000 nid=0x6394 runnable  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE"Thread-0" #14 prio=5 os_prio=0 cpu=0.00ms elapsed=57.35s tid=0x0000017f44bd7800 nid=0x5304 waiting for monitor entry  [0x0000005f80cfe000]   java.lang.Thread.State: BLOCKED (on object monitor)        at com.company.threads.Shared.test2(Deadlock.java:40)        - waiting to lock  (a com.company.threads.Shared)        at com.company.threads.Shared.test1(Deadlock.java:33)        - locked  (a com.company.threads.Shared)        at com.company.threads.Thread1.run(Deadlock.java:67)"Thread-1" #15 prio=5 os_prio=0 cpu=0.00ms elapsed=57.35s tid=0x0000017f44bd8800 nid=0xfa4 waiting for monitor entry  [0x0000005f80dfe000]   java.lang.Thread.State: BLOCKED (on object monitor)        at com.company.threads.Shared.test2(Deadlock.java:40)        - waiting to lock  (a com.company.threads.Shared)        at com.company.threads.Shared.test1(Deadlock.java:33)        - locked  (a com.company.threads.Shared)        at com.company.threads.Thread2.run(Deadlock.java:90)"DestroyJavaVM" #16 prio=5 os_prio=0 cpu=171.88ms elapsed=55.35s tid=0x0000017f298c9000 nid=0x38ec waiting on condition  [0x0000000000000000]   java.lang.Thread.State: RUNNABLE"VM Thread" os_prio=2 cpu=0.00ms elapsed=57.49s tid=0x0000017f43f73800 nid=0x52c4 runnable"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=57.51s tid=0x0000017f298e1000 nid=0x47dc runnable"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=57.51s tid=0x0000017f29911000 nid=0x61c4 runnable"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=57.51s tid=0x0000017f29912000 nid=0x61c0 runnable"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=57.50s tid=0x0000017f43e0a800 nid=0x1fa8 runnable"G1 Young RemSet Sampling" os_prio=2 cpu=0.00ms elapsed=57.50s tid=0x0000017f43e0b000 nid=0x47a4 runnable"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=57.36s tid=0x0000017f44b03800 nid=0x2408 waiting on conditionJNI global refs: 15, weak refs: 0Found one Java-level deadlock:============================="Thread-0":  waiting to lock monitor 0x0000017f43f87980 (object 0x000000008a2e9ce0, a com.company.threads.Shared),  which is held by "Thread-1""Thread-1":  waiting to lock monitor 0x0000017f43f87780 (object 0x000000008a2e9cd0, a com.company.threads.Shared),  which is held by "Thread-0"Java stack information for the threads listed above:==================================================="Thread-0":        at com.company.threads.Shared.test2(Deadlock.java:40)        - waiting to lock  (a com.company.threads.Shared)        at com.company.threads.Shared.test1(Deadlock.java:33)        - locked  (a com.company.threads.Shared)        at com.company.threads.Thread1.run(Deadlock.java:67)"Thread-1":        at com.company.threads.Shared.test2(Deadlock.java:40)        - waiting to lock  (a com.company.threads.Shared)        at com.company.threads.Shared.test1(Deadlock.java:33)        - locked  (a com.company.threads.Shared)        at com.company.threads.Thread2.run(Deadlock.java:90)Found 1 deadlock.

正如我们可以看到的,有明确提到,发现1死锁。在您的机器上尝试时,可能会出现相同的消息。

避免死锁状态

我们可以通过了解其可能性来避免死锁状态。这是一个非常复杂的过程,不容易捕捉。但如果我们尝试,我们仍然可以避免这种情况。有一些方法可以避免这种情况。我们不能完全消除它的可能性,但我们可以减少。

  • 避免嵌套锁: 这是死锁的主要原因。死锁主要发生在我们给多个线程加锁时。如果我们已经给多个线程加了锁,请避免给多个线程加锁。
  • 避免不必要的锁: 我们应该只锁定那些需要的成员。不必要地上锁会导致死锁。
  • 使用线程连接: 当一个线程等待另一个线程完成时,会出现死锁状态。如果出现这种情况,我们可以使用线程。加入你认为执行所需的最长时间。

要点:

  • 如果线程正在等待彼此完成,那么这种情况称为死锁。
  • 死锁条件是一种复杂的条件,只有在多个线程的情况下才会发生。
  • 死锁条件会在运行时破坏代码,并破坏业务逻辑。
  • 我们应该尽可能避免这种情况。

本文由 维沙尔·加格 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 写极客。组织 或者把你的文章寄去评论-team@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。 如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享