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. 再次记录一下,只是为了让自己以后不要再犯同样的错误!

本文通过一个具体的生产者和消费者程序案例,详细分析了Java中Condition的SignalAll和await方法的误用及其带来的问题。文章指出,不当使用这些方法会导致线程间的通信错误,进而影响程序的正常运行。
1750

被折叠的 条评论
为什么被折叠?



