死锁的例子
Example of deadlock
我查了很多网站,只有deadlock
的例子是这样的。 synchronized
块中总是有一个 synchronized
块。
(方法 withdraw
被 a
锁定,方法 deposit
被 b
锁定。)
class Account
{
int balance;
Account(int amount)
{balance = amount;}
void withdraw(int amount)
{balance-=amount;}
void deposit(int amount)
{balance+=amount;}
}
class Threaddemo extends Thread
{
Account a,b;
int amount;
Threaddemo(Account a,Account b,int amount)
{
this.a=a;this.b=b;this.amount=amount;
start();
}
public void run()
{
transfer(a,b,amount);
}
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
}
class U3
{
public static void main(String[] args)
{
Account a = new Account(1000);
Account b = new Account(2000);
new Threaddemo(a,b,100);
new Threaddemo(b,a,200);
}
}
但是如果我们在同步块之后使用同步块,就不会出现死锁。
class Account
{
int balance;
Account(int amount)
{balance = amount;}
void withdraw(int amount)
{balance-=amount;}
void deposit(int amount)
{balance+=amount;}
}
class Threaddemo extends Thread
{
Account a,b;
int amount;
Threaddemo(Account a,Account b,int amount)
{
this.a=a;this.b=b;this.amount=amount;
start();
}
public void run()
{
transfer(a,b,amount);
}
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
class U3
{
public static void main(String[] args)
{
Account a = new Account(1000);
Account b = new Account(2000);
new Threaddemo(a,b,100);
new Threaddemo(b,a,200);
}
}
如果这是导致死锁的唯一方法,那我们为什么不使用两个单独的同步块呢?
如果还有其他的死锁方法请举例说明。
考虑一家拥有数千个 Account
类型银行账户的银行。现在让我们看看为什么这段代码会导致死锁:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
设线程tA
和线程tB
。如果线程 tA
运行以下代码
transfer(accountA, AccountB)
当线程 tB
同时运行 transfer(accountB, accountA)
时,可能会出现死锁,因为如果我们查看以下顺序:
tA: synchronized(accountA)
tB: synchronized(accountB)
tA: tries to lock object AccountB
, but lock is held by tB => deadlock
我们看到两者之间存在循环依赖关系,不允许其中一个线程继续前进。
如果我们查看您更新后的代码:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
我们必须采用以下假设:
账户a的资金是无限的,可能是a.balance <
amount,这意味着 a.balance < 0,这打破了我们的不变量
总是有余额>=0.
- 我们允许不一致,例如如果我们要汇总所有当前现金,我们将汇总少于实际金额,因为您当前的代码允许我们这样做。
如果我们尝试修复代码,我们必须在更新余额之前确保 a.balance >= 金额。现在让我们看看以下场景:
Account a
has balance < amount
We must wait until a.balance >= amount
to withdraw money from Account a
As we are keeping the lock of Account a
in this thread, no other thread can ever update a.balance
=> We suffer from starvation
要解决这些问题,您必须使用监视器或条件来检查 a.balance>=amount to progress 并将线程置于阻塞状态,以便线程可以继续或更新您的代码,以便始终以相同的顺序获取锁。
解决方案 #1:获取对象锁的唯一顺序
如果我们使用唯一的顺序获取对象的锁,我们可以确保不会发生死锁,因为我们以指定的顺序获取锁,不允许任何循环依赖,否则称为死锁。
public void transfer(Account a,Account b,int amount)
{
//define a specific order, in which locks are acquired
//the id's of all accounts are unique!
if(a.id<b.id){
synchronized(a){
synchronized(b){
//do operations here
}
}
}else{
synchronized(b){
synchronized(a){
//do operations here
}
}
}
}
解决方案 #2:使用生产者-消费者模式来检查 a.balance>=amount
。
public void transfer(Account a,Account b,int amount)
{
while(true){
synchronized(a){
if(a.balance>=amount){
//do operations here
}
}
try{Thread.sleep(500);} //Use this as a backoff, as otherwise you'd likely get high congestion
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
//do operations here
}
}
我查了很多网站,只有deadlock
的例子是这样的。 synchronized
块中总是有一个 synchronized
块。
(方法 withdraw
被 a
锁定,方法 deposit
被 b
锁定。)
class Account
{
int balance;
Account(int amount)
{balance = amount;}
void withdraw(int amount)
{balance-=amount;}
void deposit(int amount)
{balance+=amount;}
}
class Threaddemo extends Thread
{
Account a,b;
int amount;
Threaddemo(Account a,Account b,int amount)
{
this.a=a;this.b=b;this.amount=amount;
start();
}
public void run()
{
transfer(a,b,amount);
}
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
}
class U3
{
public static void main(String[] args)
{
Account a = new Account(1000);
Account b = new Account(2000);
new Threaddemo(a,b,100);
new Threaddemo(b,a,200);
}
}
但是如果我们在同步块之后使用同步块,就不会出现死锁。
class Account
{
int balance;
Account(int amount)
{balance = amount;}
void withdraw(int amount)
{balance-=amount;}
void deposit(int amount)
{balance+=amount;}
}
class Threaddemo extends Thread
{
Account a,b;
int amount;
Threaddemo(Account a,Account b,int amount)
{
this.a=a;this.b=b;this.amount=amount;
start();
}
public void run()
{
transfer(a,b,amount);
}
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
class U3
{
public static void main(String[] args)
{
Account a = new Account(1000);
Account b = new Account(2000);
new Threaddemo(a,b,100);
new Threaddemo(b,a,200);
}
}
如果这是导致死锁的唯一方法,那我们为什么不使用两个单独的同步块呢? 如果还有其他的死锁方法请举例说明。
考虑一家拥有数千个 Account
类型银行账户的银行。现在让我们看看为什么这段代码会导致死锁:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
}
设线程tA
和线程tB
。如果线程 tA
运行以下代码
transfer(accountA, AccountB)
当线程 tB
同时运行 transfer(accountB, accountA)
时,可能会出现死锁,因为如果我们查看以下顺序:
tA:
synchronized(accountA)
tB:
synchronized(accountB)
tA: tries to lock object
AccountB
, but lock is held by tB => deadlock
我们看到两者之间存在循环依赖关系,不允许其中一个线程继续前进。
如果我们查看您更新后的代码:
public void transfer(Account a,Account b,int amount)
{
synchronized(a)
{
a.withdraw(amount);
System.out.print(amount+" is withdrawn from account a\n");
try{Thread.sleep(500);}
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
b.deposit(amount);
System.out.print(amount+" is deposited into account b\n");
}
}
我们必须采用以下假设:
账户a的资金是无限的,可能是a.balance < amount,这意味着 a.balance < 0,这打破了我们的不变量 总是有余额>=0.
- 我们允许不一致,例如如果我们要汇总所有当前现金,我们将汇总少于实际金额,因为您当前的代码允许我们这样做。
如果我们尝试修复代码,我们必须在更新余额之前确保 a.balance >= 金额。现在让我们看看以下场景:
Account
a
hasbalance < amount
We must wait until
a.balance >= amount
to withdraw money from Accounta
As we are keeping the lock of Account
a
in this thread, no other thread can ever updatea.balance
=> We suffer from starvation
要解决这些问题,您必须使用监视器或条件来检查 a.balance>=amount to progress 并将线程置于阻塞状态,以便线程可以继续或更新您的代码,以便始终以相同的顺序获取锁。
解决方案 #1:获取对象锁的唯一顺序
如果我们使用唯一的顺序获取对象的锁,我们可以确保不会发生死锁,因为我们以指定的顺序获取锁,不允许任何循环依赖,否则称为死锁。
public void transfer(Account a,Account b,int amount)
{
//define a specific order, in which locks are acquired
//the id's of all accounts are unique!
if(a.id<b.id){
synchronized(a){
synchronized(b){
//do operations here
}
}
}else{
synchronized(b){
synchronized(a){
//do operations here
}
}
}
}
解决方案 #2:使用生产者-消费者模式来检查 a.balance>=amount
。
public void transfer(Account a,Account b,int amount)
{
while(true){
synchronized(a){
if(a.balance>=amount){
//do operations here
}
}
try{Thread.sleep(500);} //Use this as a backoff, as otherwise you'd likely get high congestion
catch(Exception e){System.out.println(e);}
}
synchronized(b)
{
//do operations here
}
}