7-2小记

    技术2022-07-13  69

    第六讲 - 用"等待-通知"机制优化循环等待

    引出问题:

           上一篇中,破坏死锁条件之一:占用且等待条件,采用的方式是一次性申请所有资源,并且死循环等待资源;

           代码如下:如果申请资源耗时长,并发量大,要循环上万次才能获取锁,非常消耗CPU;

          

     

    解决方案:

           使用线程阻塞的方式避免循环等待消耗CPU问题;当线程要求的条件不满足时,线程应该阻塞自己, 进入

           等待状态,当线程要求的条件满足后,通知等待的线程重新执行;

     

    "等待-通知"机制:

            方法一:java语言内置synchronized配合wait()、notify()、notifyAll();

            0)等待队列与互斥锁是一对一关系,每个互斥锁都有自己的等待队列;

            1)同一时刻,只允许一个线程进入synchronized保护的临界区,当有一个线程进入临界区后,其他线程就

                 只能在左边的等待队列里等待;

           2)当一个线程进入临界区后,由于某些条件不满足,需要进入等待状态,java对象的wait()方法就能满足这

                种需求,调用wait()方法后,当前线程被阻塞,并且进入右边的等待队列,右边的等待队列也是互斥锁的

                等待队列;

           3)线程在进入等待队列的同时,会释放持有的互斥锁,线程释放锁后,其他线程就有机会获取锁,进入临

                界区;

           5)当满足条件时,调用java对象的notify()、notifyAll()方法通知等待队列中的线程,告诉它曾经满足过;

           6)notify()、notifyAll()只能保证在通知的时间点条件是满足的,而被通知线程的执行时间点和通知时间点基

                本上不会重合,所以当线程执行的时候,很可能条件已经不满足了;

           7)被通知的线程想要重新执行,仍然需要获取到互斥锁,因为曾经获取的锁在调用wait()时已经释放了;   

           8)wait()、notify()、notifyAll()方法操作的等待队列是互斥锁的等待队列;

                如果synchronized锁定的是this,那么对应的一定是this.wait()、this.notify()、this.notifyAll();

                如果synchronized锁定的是target,那么对应的一定是target.wait()、target.notify()、target.notifyAll();

           9)wait()、notify()、notifyAll能被调用的前提是已经获得了相应的互斥锁,所以在synchronized内部被调用;    

           

     

    案例分享:

          解决一次性申请转出账户和转入账户问题:

          1)互斥锁:因为Allocator是单例的,可以用this作为互斥锁;

          2)线程要求的条件:转出账户和转入账户都没有被分配过;

          3)何时等待:线程要求的条件不满足就等待;

          4)何时通知:当有线程释放账户时就通知;

          

             

           

               

         

       

         

           

     

    Processed: 0.014, SQL: 9