同步的 关键字用于使类或方法线程安全,这意味着只有一个线程可以拥有同步方法的锁并使用它,其他线程必须等待锁释放,并且其中任何线程都可以获得该锁。 如果我们的程序运行在两个或多个线程同时执行的多线程环境中,那么使用它是很重要的。但有时它也会引起一个问题,叫做 僵局 下面是死锁条件的一个简单示例。
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运行上述程序。我们可以复制源代码并在本地机器上运行。我们可以看到它无限期地运行,因为线程处于死锁状态,不允许代码执行。现在让我们一步一步地看看那里发生了什么。
- 线程t1通过获取s1的对象锁来启动并调用test1方法。
- 线程t2通过获取s2的对象锁来启动并调用test1方法。
- t1打印test1 begin,t2打印test-2 begin,两个线程都等待1秒,这样两个线程中的任何一个都可以启动。
- t1试图获取s2的对象锁并调用方法test2,但由于t2已经获取了它,所以它会等待它释放。在得到s2锁之前,它不会释放s1锁。
- t2也是如此。它试图获取s1的对象锁并调用方法test1,但t1已经获取了它,所以它必须等到t1释放锁。t2也不会释放s2的锁,直到它获得s1的锁。
- 现在,两个线程都处于等待状态,等待对方释放锁。现在有一个竞争条件,谁将首先释放锁。
- 因为它们都没有准备好释放锁,所以这是死锁状态。
- 当你运行这个程序时,看起来执行暂停了。
检测死锁状态
我们也可以通过在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主页上,并帮助其他极客。 如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。