java19(多线程--05 经典案例之生产者和消费者问题分析)

        如下图代码所示,生产者Producer类和消费者Consumer类共同操作同一个资源resource,二者产生了如下所示的线程安全问题

13091615-a95efefc33453338.png
13091615-009013d72bde4139.png

分析:

        上面的例子中,我们开启了4个线程,其中线程t1,t2是生产者。t3,t4是消费者

13091615-af7fbe564bc87614.png


    (1)线程t1正常生产后进入阻塞状态   

        假设开始的时候:t1进入resource类中,flag初始值为false,t1直接给name赋值,然后执行notify()(线程池此时为空)。此时t1还处于运行状态,因此run方法还会从头再执行,而此时flag变为true,t1进入阻塞状态wait()

    (2)线程t2进入阻塞状态

        当线程t1进入阻塞状态后,此时t2,t3,t4线程都处于就绪状态。这里我们假设t2进入运行状态,此时flag为真,t2也进入阻塞状态wait()

    (3)线程t3正常消费后进入阻塞状态,并唤醒线程t1

        t3正常消费,唤醒线程池中的t1(此时t1处于就绪状态)t3还处于运行状态,flag变为false,t3进入阻塞状态wait()

    (4)线程t1与线程t4争抢执行权,t4进入阻塞状态

        由于flag为false,线程t4进入阻塞状态。线程t1开始执行

    (5)t1正常生产,唤醒最早进入线程池中的t2,并进入阻塞状态

    (6)t2异常生产

        此时线程中仅剩t2处于就绪状态,t2不会重新判断flag,而是继续执行wait()之后的代码    

13091615-c5dcf70ea39d4d63.png

解决方案1:追加自旋锁

        判断flag真假可以使用 while (condition) {},不能使用 if(condition) {}。其中 while(condition)循环,它又被叫做“自旋锁”。为防止该线程没有收到notify()调用也从wait()中返回(也称作虚假唤醒),这个线程会重新去检查condition条件以决定当前是否可以安全地继续执行还是需要重新保持等待,而不是认为线程被唤醒了就可以安全地继续执行了。被唤醒的线程会自旋直到自旋锁(while循环)里的条件变为false(这种做法要慎重,目前的JVM实现自旋会消耗太大的CPU)

13091615-2be3459781ed8114.png

解决方案2: JDK1.5版本新特性

        ReentrantLock(重入锁):效果synchronized一样,都可以同步执行,lock方法获得锁,unlock方法释放锁

        Condition类的awiat方法和Object类的wait方法等效

        Condition类的signal方法和Object类的notify方法等效

        Condition类的signalAll方法和Object类的notifyAll方法等效

13091615-f5533bda4b480c95.png

猜你喜欢

转载自blog.csdn.net/weixin_34167819/article/details/87054192