多线程并发编程-死锁问题和对应的解决方案汇总-面试常考

一、首先死锁需要满足以下几个条件:

      多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从网上其他文档看到的死锁产生的四个必要条件:

  • 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();


    }
}

猜你喜欢

转载自blog.csdn.net/jason_jiahongfei/article/details/106308331