鱼友3172
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方法唤醒等待队列里的线程。
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP