記事ディレクトリ
2つの転送のデッドロックデモ
2つのロックが必要です:送金する場合、アカウントをロックし、同時に1つのスレッドのみが送金を実行できます
。2つのロックが正常に取得され、残高が0より大きい場合、送金者の金額が差し引かれ、受取人の金額が増加します。そしてそれはアトミック操作です。
デッドロックの状況があります。相手が私に送金し、私も相手に送金すると、両方の当事者が自分のロックを保持し、相手のロックが必要になるため、デッドロックが発生します。
次のコードは、転送デッドロックの発生を示しています。run
メソッドでは、異なるスレッドのフラグに応じて異なる転送メソッドが実行され、aとbは互いに転送されます
。transferMoneyメソッドはtransferメソッドです。
それぞれtransferメソッドでは転送者と受信者の2つのロックを取得するには、転送を実行できます。
package com.thread.deadlock;
/**
* 类名称:TransferMoney
* 类描述: 转账时遇到死锁的代码演示
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2020/9/8 19:24
* Version 1.0
*/
public class TransferMoney implements Runnable {
//根据不同的flag, 给不同的人转账
int flag = 1 ;
static Account a = new Account(500);
static Account b = new Account(500);
public static void main(String[] args) throws InterruptedException {
TransferMoney r1 = new TransferMoney();
TransferMoney r2 = new TransferMoney();
r1.flag = 1;
r2.flag = 0;
Thread thread1 = new Thread(r1);
Thread thread2 = new Thread(r2);
thread1.start();
thread2.start();
//主线程等待子线程执行完毕
thread1.join();
thread2.join();
System.out.println("a的余额 " +a.balance);
System.out.println("b的余额 " +b.balance);
}
@Override
public void run() {
//flag 是1 则 a 给b钱
if (flag == 1) {
transferMoney(a, b, 200);
}
//flag 是0 则b 给a钱
if (flag == 0) {
transferMoney(b, a, 200);
}
}
/**
* 转账的方法
* @param from 转账方
* @param to 收账方
* @param amount 金额
*/
public static void transferMoney(Account from, Account to, int amount) {
synchronized (from) {
synchronized (to) {
//转账前判断余额是否充足
if (from.balance - amount < 0) {
System.out.println("余额不足, 转账失败"+Thread.currentThread().getName());
return;
}
//余额充足,则进行转账操作. 转账方扣钱, 收款方收钱
from.balance -=amount;
to.balance +=amount;
System.out.println("成功转账" +amount +"元 "+Thread.currentThread().getName());
}
}
}
//账户的静态内部类
static class Account {
//余额
int balance;
public Account(int balance) {
this.balance = balance;
}
}
}
プログラムを実行すると、この時点で転送操作が正常に実行されます。0と1という名前のスレッドはそれぞれtransferメソッドで実行された転送に行き、200を相互に転送するため、バランスは変更されません。
次のように変更します。
package com.thread.deadlock;
/**
* 类名称:TransferMoney
* 类描述: 转账时遇到死锁的代码演示
*
* @author: https://javaweixin6.blog.csdn.net/
* 创建时间:2020/9/8 19:24
* Version 1.0
*/
public class TransferMoney implements Runnable {
//根据不同的flag, 给不同的人转账
int flag = 1 ;
static Account a = new Account(500);
static Account b = new Account(500);
public static void main(String[] args) throws InterruptedException {
TransferMoney r1 = new TransferMoney();
TransferMoney r2 = new TransferMoney();
r1.flag = 1;
r2.flag = 0;
Thread thread1 = new Thread(r1);
Thread thread2 = new Thread(r2);
thread1.start();
thread2.start();
//主线程等待子线程执行完毕
thread1.join();
thread2.join();
System.out.println("a的余额 " +a.balance);
System.out.println("b的余额 " +b.balance);
}
@Override
public void run() {
//flag 是1 则 a 给b钱
if (flag == 1) {
transferMoney(a, b, 200);
}
//flag 是0 则b 给a钱
if (flag == 0) {
transferMoney(b, a, 200);
}
}
/**
* 转账的方法
* @param from 转账方
* @param to 收账方
* @param amount 金额
*/
public static void transferMoney(Account from, Account to, int amount) {
synchronized (from) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (to) {
//转账前判断余额是否充足
if (from.balance - amount < 0) {
System.out.println("余额不足, 转账失败"+Thread.currentThread().getName());
return;
}
//余额充足,则进行转账操作. 转账方扣钱, 收款方收钱
from.balance -=amount;
to.balance +=amount;
System.out.println("成功转账" +amount +"元 "+Thread.currentThread().getName());
}
}
}
//账户的静态内部类
static class Account {
//余额
int balance;
public Account(int balance) {
this.balance = balance;
}
}
}
2番目のロックを取得する前に、現在のスレッドを20ミリ秒間スリープさせるように変更しました。
この時点でプログラムを実行すると、コンソールに何も表示されず、赤いボタンが常にオンになっていて、デッドロック状態に入ったことを示しています。
両方のスレッド上の図の67行目までしか実行できないため、コードを実行できません。
デッドロックの原因を分析します。最初のスレッドが次のメソッドに入ると、からロックを取得してスリープし、timed_waiting状態になります。 sleep、fromからロックを解放しません。
このとき、CPUは2番目のスレッドをスケジュールして実行し、2番目のスレッドのfromは最初のスレッドのtoロックです。2番目のスレッドがtoロックを取得した後、timed_waiting状態になります、toロックは解放されません。
その後、CPUは最初のスレッドに戻り、toロックを取得しようとしていますが、2番目のスレッドによって保持されているため、取得できません
。2番目のスレッドはtoロックを保持し、 fromロックを取得できない場合は、デッドロック状態に入ります。
デッドロックが発生する理由は、次のメソッドでは、ロック取得の順序が逆になっているためです。
補足:上記のデモコードは、2つのアカウントクラスに対して作成されますすべてが静的オブジェクトであり、静的オブジェクトはJava仮想マシン全体で1つのインスタンスしか持たないため、異なるスレッドがロックを取得する場合、常に1つのスレッドのみがロックを取得します。この状況は、単一のプロジェクトに適用されます。ロックを取得する状況は異なり、それに対処するには別の方法が必要です。