Java后端
·2025-11-21学习笔记
我们直接看破坏占有且等待条件的代码
public Class Allocator{
private static final Allocator INSTANCE = new Allocator();
public static Allocator getInstance() {
return INSTANCE;
}
private Allocator() {
als = new ArrayList<>();
}
private List<Object> als;
synchronized boolean apply(Object from,Object to){
if(als.constains(from)||als.constains(to)){
returan false
}else{
als.add(from);
als.add(to);
}
return true;
}
synchronized void free(Object from,Object to){
als.remove(from);
als.remove(to);
}
}
public Class Account{
//这里假设actr是个单例,
private Allocator actr; = Allocator.getInstance;
private int balance;
void transfer(Account actr,int amt){
while(!actr.apply(from,to));
try{
synchronize(this){
synchronized(from){
if(this.balance>amt){
this.balance-=amt;
from.balance+=amt;
}
}
}
}finally{
atcr.free(this,from);
}
}
}
这段代码的执行流程:用户A向用户B转账时,会先去让Allocator去拿锁A和锁B的使用权,Allocator调用apply去拿锁A和锁B的使用权时,还得先获取自己的锁,然后再去拿锁A和锁B的使用权,如果Allocator没同时拿到两个锁的使用权,用户A就会使用while再次去让Allocator去拿锁的使用权,直到Allocator同时拿到锁A和锁B的使用权时。用户A再向下执行,去获取锁A和锁B,此时其他线程是无法去抢到锁A和锁B的,因为其他线程也要向A或B进行转账,那么它们也要让Allocator去拿锁A或锁B的使用权,但是此时锁的使用权已经被A拿到了,所以它们没有使用权的情况下,不会去抢夺锁。拿到锁A和锁B后,用户A执行完转账流程,就将两个锁释放,然后再把锁A和锁B的使用权释放。
以上这段代码有个缺点,就是再忙等待,线程在调用transfer时,会不停的去调用apply方法,一直在忙,一直在调用CPU,但是却没有进展,这就是忙等待。
我们还可以优化一下:
public Class Allocator{
private static final Allocator INSTANCE = new Allocator();
public static Allocator getInstance() {
return INSTANCE;
}
private Allocator() {
als = new ArrayList<>();
}
private List<Object> als;
synchronized void apply(Object from,Object to){
while(als.constains(from)||als.constains(to)){
try{
wait();
}catch(Exception e){
}
als.add(from);
als.add(to);
}
}
synchronized void free(Object from,Object to){
als.remove(from);
als.remove(to);
notifyAll();
}
}
public Class Account{
//这里假设actr是个单例,
private Allocator actr; = Allocator.getInstance;
private int balance;
void transfer(Account actr,int amt){
actr.apply(from,to);
try{
synchronize(this){
synchronized(from){
if(this.balance>amt){
this.balance-=amt;
from.balance+=amt;
}
}
}
}finally{
atcr.free(this,from);
}
}
}
这是修改后的方法
将Allocator里的apply方法改为了void,if变为while,并且在里面用了wait,然后free也使用了notifyAll而Account也不用while去调用apply方法了,而是直接调用apply,只调用一次。我们来详细讲讲
首先wait和notifyAll是个什么?
1.它和notifyAll以及notify得在synchronized临界区里面调用,否则jvm会报错
2.执行wait之后,会释放当前持有的锁,然后进入等待队列。
3.notifyAll会通知唤醒所有等待队列中的线程,而notify则是随机通知唤醒一个线程
4.等待队列中的线程被唤醒后,会在当前wait的代码行被唤醒,继续执行代码逻辑。
我们来执行一遍代码的逻辑,例如线程T1执行账户A向账户B转账。账户A调用transfer方法,然后去获取锁,执行一次apply方法,apply里去执行while方法,去获取锁A和锁B的使用权,如果当前无法同时获取锁A和锁B的使用权,那就执行wait方法,进入等待队列,随后等待被唤醒,线程T1被唤醒后,继续往下执行while里的apply,然后又要重新去获取锁,,但是发现又没有使用权,于是继续睡(这也是为什么要把if改为while,因为线程被唤醒!=线程拿到使用权,因为线程被唤醒之后,要重新去抢apply方法的锁)。直到拿到两个锁的使用权,然后去获取两个锁,执行转账逻辑,最后释放两个锁,然后把两个锁的使用权也释放掉,然后执行notifyAll方法唤醒等待队列里的线程。
1
0
分享
操作
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
