解决多线程死锁问题

什么是死锁

当两个或两个以上的线程互相持有对方需要的资源的时候。并且对获取到资源不会主动释放,造成多线程互相等待的情况。

死锁产生的条件

**互斥条件:**一个资源或者说一个锁,只能同时被一个线程获取,当一个线程获取到之后,在该线程没有释放之前,其他线程只能等待。
**占有且等待:**一个线程已经获取到一个锁,在获取另一个锁时,就算获取不到等待过程中也不会释放已经获取到的锁资源。
**不可剥夺条件:**任何一个线程都无法强制剥夺其他线程已经获取到的锁资源。
**循环等待条件:**线程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);
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值