一、首先死锁需要满足以下几个条件:
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从网上其他文档看到的死锁产生的四个必要条件:
- 1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
- 2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
- 3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
- 4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。下面用java代码来模拟一下死锁的产生。
二、解决办法:
只要打破四个必要条件中的一个,当然互斥不能打破,否则就会带来线程安全性问题,破坏原子性。
1、死锁“车祸”现场,代码示例:
package com.jason.thread.deadthread;
import static java.lang.Thread.sleep;
/**
* @program: JasonSpringBoot
* @description
* @author: 大龄程序猿
* @create: 2020-05-23 23:50
**/
public class TestMain {
public static void main(String[] args) {
Thread thread1=new Thread(new Thread1(),"thread1");
Thread thread2=new Thread(new Thread2(),"thread2");
thread1.start();
thread2.start();
}
}
class Thread1 implements Runnable
{
@Override
public void run() {
try {
synchronized (Lock.lock1) {
System.out.println("Thread1 lock resource1");
Thread.sleep(2000);
synchronized (Lock.lock2) {
System.out.println("Thread1 lock resource2");
}
System.out.println("Thread1 release resource2");
}
System.out.println("Thread1 release resource1");
}catch (Exception e)
{
e.printStackTrace();
}
}
}
class Thread2 implements Runnable
{
@Override
public void run() {
try {
synchronized (Lock.lock2) {
System.out.println("Thread2 lock resource2");
Thread.sleep(2000);
synchronized (Lock.lock1) {
System.out.println("Thread2 lock resource1");
}
System.out.println("Thread2 release resource1");
}
System.out.println("Thread2 release resource2");
}catch (Exception e)
{
e.printStackTrace();
}
}
}
class Lock{
static Object lock1=new Object();
static Object lock2=new Object();
}
运行结果:
Thread1 lock resource1
Thread2 lock resource2
代码阻塞
使用jdk自带的工具,jvisualvm.exe,分析jvm信息:
2、破坏死锁:
1)场景一:
不可抢占:
package com.jason.thread.deadthread;
import java.lang.Thread.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @program: JasonSpringBoot
* @description
* @author: 大龄程序猿
* @create: 2020-05-23 23:50
**/
public class TestMain {
public static void main(String[] args) {
Thread thread1=new Thread(new Thread1(),"thread1");
Thread thread2=new Thread(new Thread2(),"thread2");
thread1.start();
thread2.start();
}
}
class Thread1 implements Runnable
{
@Override
public void run() {
try {
if(Locks.lock1.tryLock())
{
System.out.println("Thread2 lock resource2");
Thread.sleep(2000);
if (Locks.lock2.tryLock()) {
System.out.println("Thread2 lock resource1");
}
System.out.println("Thread2 release resource1");
}
System.out.println("Thread2 release resource2");
if(Locks.lock1.tryLock()){
System.out.println("Thread1 lock resource1");
Thread.sleep(2000);
if(Locks.lock2.tryLock()){
System.out.println("Thread1 lock resource2");
}
System.out.println("Thread1 release resource2");
}
System.out.println("Thread1 release resource1");
}catch (Exception e)
{
e.printStackTrace();
}
}
}
class Thread2 implements Runnable
{
@Override
public void run() {
try {
if(Locks.lock2.tryLock())
{
System.out.println("Thread2 lock resource2");
Thread.sleep(2000);
if (Locks.lock1.tryLock()) {
System.out.println("Thread2 lock resource1");
}
System.out.println("Thread2 release resource1");
}
System.out.println("Thread2 release resource2");
}catch (Exception e)
{
e.printStackTrace();
}
}
}
class Locks{
// static Object lock1=new Object();
// static Object lock2=new Object();
static Lock lock1=new ReentrantLock();
static Lock lock2=new ReentrantLock();
}
2)场景二:
请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有
解决思路:
引入一个管理类,管理锁的申请和释放;
package com.jason.thread.deadthread;
import java.util.ArrayList;
import java.util.List;
/**
* @program: JasonSpringBoot
* @description
* @author: 大龄程序猿
* @create: 2020-05-24 16:17
**/
public class TransLocal {
public List<Account> accountList=new ArrayList<Account>();
synchronized boolean applyLock(Account account,Account account1)
{
if(accountList.contains(account)||accountList.contains(account1))
{
return false;
}
accountList.add(account);
accountList.add(account1);
return true;
}
synchronized void removeLock()
{
accountList.clear();
}
}
package com.jason.thread.deadthread;
/**
* @program: JasonSpringBoot
* @description
* @author: 大龄程序猿
* @create: 2020-05-24 14:37
**/
public class TransAccount implements Runnable {
private Account fromAccount;
private Account toAccount;
private long amount;
private TransLocal locks;
public TransAccount(Account fromAccount, Account toAccount, long amount,TransLocal locks) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
this.locks=locks;
}
@Override
public void run() {
while(true)
{
if(locks.applyLock(fromAccount,toAccount))
{
try
{
synchronized (fromAccount)
{
synchronized (toAccount)
{
if(fromAccount.getAmount()>=amount)
{
fromAccount.withDraw(amount);
toAccount.deposit(amount);
}
}
}
System.out.println(fromAccount.getAccountName()+"--balance=-->"+fromAccount.getAmount());
System.out.println(toAccount.getAccountName()+"--balance=-->"+toAccount.getAmount());
}finally {
locks.removeLock();
}
}
}
}
public static void main(String[] args) {
Account fromAccount=new Account("jason",3000000);
Account toAccount=new Account("jerry",100000);
TransLocal locks=new TransLocal();
Thread thread1=new Thread(new TransAccount(fromAccount,toAccount,30,locks));
Thread thread2=new Thread(new TransAccount(toAccount,fromAccount,10,locks));
thread1.start();
thread2.start();
}
}
3)场景三:
循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
解决思路:
可以使用顺序加锁,解决循环等待的问题,例如有A,B两把锁,系统控制申请锁的顺序只能是先申请A,再申请B。
package com.jason.thread.deadthread;
/**
* @program: JasonSpringBoot
* @description
* @author: 大龄程序猿 解决循环等待问题
* @create: 2020-05-24 14:37
**/
public class TransAccount2 implements Runnable {
private Account fromAccount;
private Account toAccount;
private long amount;
private TransLocal locks;
public TransAccount2(Account fromAccount, Account toAccount, long amount, TransLocal locks) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
this.locks=locks;
}
@Override
public void run() {
Account left=null;
Account right=null;
while(true)
{
if(fromAccount.hashCode()>toAccount.hashCode())
{
left=toAccount;
right=fromAccount;
}else
{
left=fromAccount;
right=toAccount;
}
synchronized (left)
{
synchronized (right)
{
if(fromAccount.getAmount()>=amount)
{
fromAccount.withDraw(amount);
toAccount.deposit(amount);
}
}
}
System.out.println(fromAccount.getAccountName()+"--balance=-->"+fromAccount.getAmount());
System.out.println(toAccount.getAccountName()+"--balance=-->"+toAccount.getAmount());
}
}
public static void main(String[] args) {
Account fromAccount=new Account("jason",3000000);
Account toAccount=new Account("jerry",100000);
TransLocal locks=new TransLocal();
Thread thread1=new Thread(new TransAccount2(fromAccount,toAccount,30,locks));
Thread thread2=new Thread(new TransAccount2(toAccount,fromAccount,10,locks));
thread1.start();
thread2.start();
}
}