zoukankan      html  css  js  c++  java
  • Mutex, Semaphore and Monitor (2)

        在上篇文章的最后,我们描述了CV(条件变量)的定义和使用方式,也曾说过Monitor事实上是基于CV的。那么,Monitor到底是怎样一种机制呢?

        其实,与其说Monitor是一种机制,倒不如说它是一种风格(style),因为它并不是一种新的同步机制。Monitor所做的,就是把mutexCV封装在一个对象里面,来保护这个对象的共有数据的访问,如下图所示:

    其中Lock控制线程的进入,保证只有一个对象能拿到锁;而CV负责线程的等待、唤醒等操作;putget是对shared data的一组访问方法。这种形式就是Monitor

        看到这个模型,你可能马上就想到,这不就是Java里面 最常见的线程间同步机制么?没错,就是这样。JavaObject类提供了一套方法:wait/notify/notifyAll,并且通过在成员方法钱加synchronized方法来实现mutex,是典型的Monitor。而且,可以看出,MonitorJava最推荐使用的线程同步方式(使用关键字的形式和把wait/signal/broadcast实现在Object中,相比之下,信号量类就放在java.util.concurrent包里)。

        使用Monitor的好处在于,mutexCV对用户都是透明的。用户只需知道,处于synchronized保护下的代码都是互斥的,而线程在对象上进行等待或着被唤醒。所以,我们可以很方便的改写前一篇文章中,使用pthreadC程序。

    public class Counter {
    
        private int count;
        
        public Counter(int count) {
            this.count = count;
        }
        public int get() {
            return this.count;
        }
        public void incr() {
            this.count++;
        }
    }
    
    class Checker implements Runnable {
    
        private Counter counter;
        
        public Checker(Counter c) {
            this.counter = c;
        }
        
        @Override
        public void run() {
        
            synchronized (counter) {
                System.out.println("The Checker get the lock and the count is " + counter.get());
                while(counter.get() < 10)
                try {
                    counter.wait();
                    System.out.println("The Checker is notified and re-get the lock, now the count is " + counter.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Increaser implements Runnable {
    
        private Counter counter;
        
        public Increaser(Counter c) {
            this.counter = c;
        }
        @Override
        public void run() {
        
            while (counter.get() < 100) {
                synchronized (counter) {
                    System.out.println("Increaser get the lock and the count is "+ counter.get());
                    counter.incr();
                    if (counter.get() >= 10) {
                        counter.notifyAll();
                    }
                }
            }
        }
    }

      在main函数里面开启一个Checker和一个Increaser后运行,观察打印结果,会发现一个很有趣的现象,Checker的第二次打印,即在wait被唤醒之后打印的count并不是10,也就是说当Counter==10后,Increaser调用了notifyAll,但是Checker并没有恢复执行。为什么呢?原因我们在上篇文章中也提到过。notifyAll并不会释放锁,而是当Increaser离开synchronized代码块后才会释放,但是由于Increaser会循环执行(代码里是执行100次),它会和即将唤醒的Checker再次竞争mutex锁,所以并不能保证Checker会立即得到锁醒来,甚至,很多时候,Increaser更易得到mutex锁,使得checker醒来时counter远大于10。在我的实验中,有几次运行甚至是100,即Increaser执行完了,checker才得到mutex锁。

        解决这个问题也很简单,就是在notifyAll后面加上break,这样,Increaser会跳出循环,并且释放锁。当然,这是一个比较特殊的例子。更多的时候,比如说Increaser需要在Checker做完之后再处理一些任务呢?显然,我们可以在Increaser调用notifyAll之后,调用wait,让其释放锁并等待,然后在Checker恢复执行后,再唤醒Increaser。如此往复下去,我们可以实现让线程交叉执行,甚至然照一个指定的序列执行。关于这方面,可以看下《Thinking in Java》中,线程那一章一个洗车抛光的例子。

        讲到这里,相信大家对这三种机制已经有了一个大概的认识了,剩下的就得在实际工作中去体会和总结了,实践出真知嘛。

  • 相关阅读:
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    Security and Cryptography in Python
    基于分布式锁解决定时任务重复问题
    基于Redis的Setnx实现分布式锁
    基于数据库悲观锁的分布式锁
    使用锁解决电商中的超卖
  • 原文地址:https://www.cnblogs.com/tobealion/p/4555488.html
Copyright © 2011-2022 走看看