生产者消费者(singlAll和await误用)

本文通过一个具体的生产者和消费者程序案例,详细分析了Java中Condition的SignalAll和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. 再次记录一下,只是为了让自己以后不要再犯同样的错误!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值