什么是死锁
当两个或两个以上的线程互相持有对方需要的资源的时候。并且对获取到资源不会主动释放,造成多线程互相等待的情况。
死锁产生的条件
**互斥条件:**一个资源或者说一个锁,只能同时被一个线程获取,当一个线程获取到之后,在该线程没有释放之前,其他线程只能等待。
**占有且等待:**一个线程已经获取到一个锁,在获取另一个锁时,就算获取不到等待过程中也不会释放已经获取到的锁资源。
**不可剥夺条件:**任何一个线程都无法强制剥夺其他线程已经获取到的锁资源。
**循环等待条件:**线程A拿着线程B的锁,线程B拿着线程A的锁。
通过代码演示死锁
银行账户转账类的业务最能够展示死锁问题:
账户类:
package com.example.threaddemo.account2;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @program: thread-demo
* @description: 账户类
* @author: demo
* @create: 2021-03-29 10:00
*/
@AllArgsConstructor
@Data
public class Account implements Serializable {
private static final long serialVersionUID = -300080638998260579L;
private String accountName;
private int balance;
public void debit(int account) {
balance -= account;
}
public void credit(int account) {
balance += account;
}
@Override
public String toString() {
return "Account{" +
"accountName='" + accountName + '\'' +
", balance=" + balance +
'}';
}
}
package com.example.threaddemo.account2;
import lombok.AllArgsConstructor;
/**
* @program: thread-demo
* @description:
* @author: demo
* @create: 2021-03-29 10:07
*/
@AllArgsConstructor
public class TranstrantAccount implements Runnable {
private Account fromAccount;
private Account toAccount;
private int account;
@Override
public void run() {
while (true) {
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance() >= account) {
fromAccount.debit(account);
toAccount.credit(account);
}
}
}
System.out.println(fromAccount.toString());
System.out.println(toAccount.toString());
}
}
public static void main(String[] args) {
Account fromAccount = new Account("A", 300000);
Account toAccount = new Account("B", 200000);
Thread t1 = new Thread(new TranstrantAccount(fromAccount, toAccount, 30));
Thread t2 = new Thread(new TranstrantAccount(toAccount, fromAccount, 20));
t1.start();
t2.start();
}
}
打印结果:
Account{accountName='A', balance=299990}
Account{accountName='B', balance=200010}
Account{accountName='B', balance=200010}
Account{accountName='A', balance=299960}
Account{accountName='B', balance=200040}
Account{accountName='A', balance=299930}
Account{accountName='B', balance=200070}
Account{accountName='A', balance=299960}
Account{accountName='A', balance=299900}
Account{accountName='B', balance=200080}
Account{accountName='A', balance=299890}
Account{accountName='B', balance=200110}
Account{accountName='A', balance=299860}
Account{accountName='B', balance=200140}
Account{accountName='A', balance=299830}
Account{accountName='B', balance=200080}
程序运行一会儿后就不再打印。但是程序没有结束。
如何判断是否发生死锁
在命令行使用 jps -l获得目前运行的进程以及运行的主类对应的id
E:\01大圣工作\A55项目\车型导入\2021022301\thread-demo> jps -l
7220 org.jetbrains.jps.cmdline.Launcher
9156 org.jetbrains.jps.cmdline.Launcher
11848
16472 com.example.threaddemo.account2.TranstrantAccount
9192 sun.tools.jps.Jps
在这里我们的主类运行的id是16472
再根据 jstack 16472来查看是否存在死锁,并且死锁的原因:
2021-03-29 10:24:16
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.261-b12 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000026066e03800 nid=0x4030 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=0 tid=0x0000026066e04000 nid=0x4100 waiting for monitor entry [0x000000b4254fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.threaddemo.account2.TranstrantAccount.run(TranstrantAccount.java:23)
- waiting to lock <0x00000000d76a9890> (a com.example.threaddemo.account2.Account)
- locked <0x00000000d76a98d8> (a com.example.threaddemo.account2.Account)
at java.lang.Thread.run(Thread.java:748)
"Thread-0" #12 prio=5 os_prio=0 tid=0x0000026066e02800 nid=0x4194 waiting for monitor entry [0x000000b4253fe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.threaddemo.account2.TranstrantAccount.run(TranstrantAccount.java:23)
- waiting to lock <0x00000000d76a98d8> (a com.example.threaddemo.account2.Account)
- locked <0x00000000d76a9890> (a com.example.threaddemo.account2.Account)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x0000026066e02000 nid=0x44e8 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x0000026066e0c000 nid=0x46d0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000026066e09000 nid=0x1528 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000026066df7000 nid=0x2744 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000026066df6000 nid=0x3d70 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000026066e05000 nid=0x3ca4 runnable [0x000000b424cfe000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000000d7726f18> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x00000000d7726f18> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000026064f94800 nid=0x3860 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000026064fe8000 nid=0x4a54 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000026064f62000 nid=0x3dbc in Object.wait() [0x000000b4249ff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d7508ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000000d7508ee0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000026064f5a000 nid=0x3508 in Object.wait() [0x000000b4248ff000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d7506c00> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000d7506c00> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x0000026064f32000 nid=0x4508 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000002604f02c800 nid=0x4510 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000002604f02e000 nid=0x1c54 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000002604f02f000 nid=0x42e0 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000002604f030800 nid=0x435c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000002604f032800 nid=0x105c runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x000002604f033800 nid=0x3080 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000002604f036800 nid=0x2c80 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000002604f037800 nid=0x485c runnable
"VM Periodic Task Thread" os_prio=2 tid=0x0000026066ed5800 nid=0x39c0 waiting on condition
JNI global references: 12
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000026064f61208 (object 0x00000000d76a9890, a com.example.threaddemo.account2.Account),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000026064f5e818 (object 0x00000000d76a98d8, a com.example.threaddemo.account2.Account),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.example.threaddemo.account2.TranstrantAccount.run(TranstrantAccount.java:23)
- waiting to lock <0x00000000d76a9890> (a com.example.threaddemo.account2.Account)
- locked <0x00000000d76a98d8> (a com.example.threaddemo.account2.Account)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at com.example.threaddemo.account2.TranstrantAccount.run(TranstrantAccount.java:23)
- waiting to lock <0x00000000d76a98d8> (a com.example.threaddemo.account2.Account)
- locked <0x00000000d76a9890> (a com.example.threaddemo.account2.Account)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
解决死锁问题
@AllArgsConstructor
@Data
public class Account {
private String accountName;
private int balance;
public void debit(int count) {
this.balance -= count;
}
public void credit(int count) {
this.balance += count;
}
}
package com.example.threaddemo.account;
/**
* @program: thread-demo
* @description:
* @author: demo
* @create: 2021-03-26 16:37
*/
public class TransferAccount implements Runnable {
private Account fromAccount;
private Account toAccount;
private int count;
Allocator allocator;
public TransferAccount(Account fromAccount, Account toAccount, int count, Allocator allocator) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.count = count;
this.allocator = allocator;
}
@Override
public void run() {
while (true) {
//这样做是为了统一分配资源
if (allocator.apply(fromAccount, toAccount)) {
try {
synchronized (fromAccount) {
synchronized (toAccount) {
if (fromAccount.getBalance() > count) {
fromAccount.debit(count);
toAccount.credit(count);
}
}
}
System.out.println(fromAccount.getAccountName() + "--->" + fromAccount.getBalance());
System.out.println(toAccount.getAccountName() + "---->" + toAccount.getBalance());
} finally {
allocator.free(fromAccount, toAccount);
}
}
}
}
public static void main(String[] args) {
Account fromaccount = new Account("A", 100000);
Account toaccount = new Account("B", 300000);
Allocator allocator = new Allocator();
Thread a = new Thread(new TransferAccount(fromaccount, toaccount, 100, allocator));
Thread b = new Thread(new TransferAccount(toaccount, fromaccount, 300, allocator));
a.start();
b.start();
}
}
package com.example.threaddemo.account;
import java.util.ArrayList;
import java.util.List;
/**
* @program: thread-demo
* @description:
* @author: demo
* @create: 2021-03-26 14:20
*/
public class Allocator {
private List<Object> list = new ArrayList<>();
synchronized Boolean apply(Object from, Object to) {
if (list.contains(from) || list.contains(to)) {
return false;
}
list.add(from);
list.add(to);
return true;
}
synchronized void free(Object from, Object to) {
list.remove(from);
list.remove(to);
}
}