生產者消費者(singlAll和await誤用)

Conidition中的SinglAll和await誤用

使用Condition的SinalAll和await()方法寫了一個生產者和消費者程序,功能很簡單,只是爲了理解singall和await方法:生產者線程生產message,只要broker中有消息,就會通知消費者去消費;消費者消費完message,通知生產者生產消息;
沒想到出現的問題每次不一樣,仔細分析了一下,自己沒有深刻理解這兩種方法原理,造成程序編寫有問題。記錄一下,爲了以後自己不再犯類似錯誤。
Condition主要是負責線程間通信與Object中方法類似,但是比起Object類中的方法要靈活很多,接口中定義的方法主要如下:
1. await();放棄當前獲得鎖,當前線程進入沉睡狀態
2. await(long,TimeUnit);放棄當前鎖,在規定時間內進入沉睡狀態
3. singal():喚起一個線程
4. singalAll():喚起所有線程
Condition主要是通過ReentrantLock中的newCondition()方法創建(具體內容暫不討論)。即Condition必須要與ReentrantLock結合起來使用,一個ReentrantLock可以創建多個Condition對象。

生產者和消費者

通過Condition中的await()方法和sinalAll方法模擬生產者和消費者。其中producer將消息放入broker中(List)。只要有消息,就會通知consumer去消費。
程序如下:

/**
 * producer將消息發送至storeService,
 * consumer從storeService中consume消息
 * @author tonny
 * 單例模式,確保只有一個實例
 */
public class MessageBrokerService {

    private static MessageBrokerService instance = null;

    private static List<String> oneMsgPool = new ArrayList<String>();

    private MessageBrokerService(){}

    public void putOneMessage(String msg){
        if(!oneMsgPool.isEmpty()){
            throw new IllegalArgumentException("隊列中消息個數不是一個");
        }
        oneMsgPool.add(msg);
    }

    public String getMessage(){
        return oneMsgPool.remove(0);
    }

    public void  putMessage(String msg){
        oneMsgPool.add(msg);
    }

    /**
     * 單例模式
     * @return
     */
    public static MessageBrokerService newInstance(){
        if(null == instance){
            synchronized(MessageBrokerService.class){
                if(null == instance){
                    instance = new MessageBrokerService();
                }
            }
        }
        return instance;
    }
}

生產者程序

/**
 * 生產者服務,可以有多個生產者線程調用生產者服務
 * 去生產消息
 * @author tonny
 * 生產者produce消息時,消費者通知生產者,消息已經被消費 了。
 */
public abstract class ProducerService { 
    /**
     * 生產消息的服務
     */
    public void putMessage(String msg){ 
        Lock lock = MessageBlockMechanism.getLock();
        Condition condition = MessageBlockMechanism.getLockCondition();
        try {
        //獲取對象鎖
        lock.lock();
    if(!MessageBlockMechanism.isProduceConsumeFlag()){
        // 放棄當前鎖
            condition.await();
        }   
        if(MessageBlockMechanism.isProduceConsumeFlag()){
            producerMessage(condition,msg);
        }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }    
    public abstract void producerMessage(Condition condition,String msg);
}

具體子類代碼

public class MultiProducerService extends ProducerService {
    @Override
    public void producerMessage(Condition condition ,String msg){
        MessageBrokerService.newInstance().putMessage(msg);
        // 設置consumer獲取消息標識
        MessageBlockMechanism.setProduceConsumeFlag(false);
        condition.signalAll();
    }
}

消費者代碼
/**
* 消費者服務,主要是消費者線程調用該服務去消費生產者生產的消息,
* (或者從broker中獲取消息)
* @author tonny
*
*/
public class ConsumerService {

public String consumeMessage(){
    Lock lock = MessageBlockMechanism.getLock();
    Condition condition = MessageBlockMechanism.getLockCondition();
String message = null;
    try {
    //獲取對象鎖
    lock.lock();     
if(MessageBlockMechanism.isProduceConsumeFlag()){
        condition.await();
    }
    if(!MessageBlockMechanism.isProduceConsumeFlag()){      
         message = MessageBrokerService.newInstance().getMessage();
         System.out.println("consumer當前線程["+Thread.currentThread().getName()+"] 消費消息 :"+message);
         // 設置consumer獲取消息標識
        MessageBlockMechanism.setProduceConsumeFlag(true);
            condition.signalAll(); 
    }
    } catch (Exception e) {
        e.printStackTrace();
    }finally{
        lock.unlock(); 
    }
    return message;
}

}
生產者消費者線程

public class ProducerServiceThread implements Runnable {
    private ProducerService service;

    public ProducerServiceThread(ProducerService service) {
        super();
        this.service = service;
    }
    public void run() {
        service.putMessage("hello");
    }
}
---
public class ConsumerServiceThread implements Runnable {
    private ConsumerService service;

    public ConsumerServiceThread(ConsumerService service) {
        super();
        this.service = service;
    }
    public void run() {
        service.consumeMessage();
    }

}

客戶端代碼

public class ProducerConsumerMultiTest {
    public static void main(String[] args) {
        ProducerService producer = new MultiProducerService();
        ConsumerService consumer = new ConsumerService();
        for(int i=0;i<10;i++){
            Thread t1 = new Thread(new ProducerServiceThread(producer));
            Thread t2 = new Thread(new ConsumerServiceThread(consumer));
            t1.start();
            t2.start();
        }

}

正確結果應該是生產者生產10個消息,消費者消費10個消息,但是運行幾次結果如下:

第一種結果:
consumer當前線程[Thread-3] 消費消息 :hello
consumer當前線程[Thread-7] 消費消息 :hello
consumer當前線程[Thread-11] 消費消息 :hello
consumer當前線程[Thread-15] 消費消息 :hello
consumer當前線程[Thread-19] 消費消息 :hello
consumer當前線程[Thread-5] 消費消息 :hello
consumer當前線程[Thread-9] 消費消息 :hello
consumer當前線程[Thread-13] 消費消息 :hello
consumer當前線程[Thread-17] 消費消息 :hello
consumer當前線程[Thread-1] 消費消息 :hello
第二種結果
consumer當前線程[Thread-1] 消費消息 :hello
consumer當前線程[Thread-5] 消費消息 :hello
consumer當前線程[Thread-13] 消費消息 :hello
consumer當前線程[Thread-15] 消費消息 :hello
consumer當前線程[Thread-17] 消費消息 :hello
consumer當前線程[Thread-9] 消費消息 :hello
consumer當前線程[Thread-7] 消費消息 :hello
consumer當前線程[Thread-19] 消費消息 :hello
第三種結果
consumer當前線程[Thread-5] 消費消息 :hello
consumer當前線程[Thread-13] 消費消息 :hello
consumer當前線程[Thread-9] 消費消息 :hello
consumer當前線程[Thread-17] 消費消息 :hello
consumer當前線程[Thread-7] 消費消息 :hello
consumer當前線程[Thread-11] 消費消息 :hello

原因分析如下

所有線程啓動時,生產者消費者標識produceConsumeFlag爲true。同時啓動10個生產者10個消費者。生產者和消費者採用同一個鎖。即每次只能有一個線程獲得執行鎖權限。
1. 生產者先獲得鎖,則生產者produce一條message,將標誌設置爲false。執行singalAll(不釋放鎖)喚起所有線程,unlock(釋放鎖)。此時如果是一個consumer得到競爭鎖,則消費完消息。執行singalAll和unlock(還剩下18個線程)。這種情況下,消費者競爭鎖,標誌已經爲true,則消費者執行await方法(釋放鎖),等待被喚醒。此時還是其他消費者獲得鎖,繼續等待。直到其中一個producer獲得鎖,則再次生產消息;設置標誌,釋放鎖。如果還是另外生產者獲得鎖,則同樣會執行await等待。這就會出現在某一個時刻多個producer和consumer都由於執行了await方法,出現等待狀態。在這種情況下,如果consumer執行singalAll方法,unlock後。緊接着後面都是被其他consumer獲得鎖,每個consumer發現produceConsumeFlag爲true,不去消費消息,直接釋放鎖。最後一個consumer釋放鎖之後(消費的消息不足10條),其中一個producer獲得鎖資格,生產消息後,設置標誌爲false。此時獲得鎖的只能是producer,最終都會結束。造成上述其中一種情況。
2. 場景很多,想要把他描述清楚,不是很容易。只能靠自己去理解。關鍵是程序編寫有問題。
3. 再次記錄一下,只是爲了讓自己以後不要再犯同樣的錯誤!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章