Java同步锁之小应用

在上文"浅谈Java同步锁"中,只对同步锁的相关概念做了简单阐述,没给出应用实例。本文,将结合现实中对于同步锁的需求,编个小例子。标题比较大,而实例并不一定十分恰当,请各位见谅。

 

需求: 一个消费者在不停的从queue里取消息,当没有消息时,阻塞等待,直到有消息来时,消费它。

 

简析: 乍一看,我们完全可以写个循环不断的去读它(queue),直到该queue不再empty,则消费一条消息(ps: 设个标志位, 循环去读,同理)。可是,无限的循环不仅有可能浪费资源,而且如果额外想设个timeout的话,还要多写好多代码,如需要取当前时间,进行比对等等。这时,如果我们使用wait()或wait(int timeout)让其阻塞等待,直到有其他线程执行了notify(),则可以让问题迎刃而解。

 

废话不多说,首先是我们的Consumer类。

 

import java.util.LinkedList;

/**
 * Consumer. <BR>
 * 
 * @author Michael Ma
 * @version 1.0.0
 */
public class Consumer {
	/** A lock object */
	private final Object lock;
	/**queue of message */
	private LinkedList<Object> msgQueue = new LinkedList<Object>();
	/** CLOSED message */
	public static final Object CLOSED = new Object();
	
	/**
	 * 构造函数. <BR>
	 * lock指向this指针
	 *
	 */
	public Consumer() {
		this.lock = this;
	}
	
	/**
	 * 开始消费. 如果queue中没有消息,则阻塞 <BR>
	 *
	 * @return this
	 */
	public Consumer startConsume() {
		synchronized (lock) {
			try {
				if (!msgQueue.isEmpty()) {
					return this;
				}
				
				// 将timeout设为10秒
				lock.wait(10000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		return this;
	}
	
	/**
	 * 从queue中读消息 <BR>
	 * 
	 * @return message content
	 * @throws Exception e
	 */
	public Object getMessage() throws Exception{
		synchronized (lock) {
			// 从queue中取一条消息
			Object message = msgQueue.poll();

			// 若该消息为Error, 抛出
			if (message instanceof Exception) {
                throw (Exception) message;
            }
			
			// 返回null如果接到CLOSED消息,或message本身为空
			if (message == null
					|| message == CLOSED) {
				return null;
			}
			
			return message;
		}
	}
	
	/**
	 * 向Queue里添加消息, notfify等待中的线程 <BR>
	 *
	 * @param message Object
	 */
	public void setMessage(Object message) {
		synchronized (lock) {
			// 向Queue中添加消息
			msgQueue.add(message);
			
			// notify等待中的线程
			lock.notify();
		}
	}
	
	/**
	 * 关闭消息 <BR>
	 */
	public void setClosed() {
		setMessage(CLOSED);
    }
}

 测试类, 比较简单,就不加注释啦

 

public class ConsumerTest extends Thread {
	Consumer c = null;
	
	public ConsumerTest(Consumer c) {
		this.c = c;
	}
	
	public static void main(String[] args) throws Exception {
		Consumer c = new Consumer();
		// 开一个线程,去往queue里添加消息
		ConsumerTest test = new ConsumerTest(c);
		test.start();
		Object msg;
		while ((msg = c.startConsume().getMessage()) != null) {
			System.out.println(msg);
		}
	}
	
	public void run() {
		try {
			sleep(3000);
			c.setMessage(new String("abc"));
			sleep(2000);
			c.setMessage(new String("def"));
			c.setMessage(Consumer.CLOSED);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

 

小结: 开始编这个例子时,还是挺容犯些小错误的。

1. 首先应该注意synchronized的对象,和wait,notify的对象,是不是相同的, 否则会报llegalMonitorStateException异常。 (解释请参考上文)。 

2. 各不同线程操作的对象是不是一个,若notify该类的其他对象则是没用的,当然,如果该类被设计为单体类,就没有这个后顾之忧啦。

3. notifyAll是唤醒在该对象上所有在waiting中的线程,适当场合时应用。(例如一个公告牌,很多线程都在等最新的消息,当公告消息来时,唤醒所有读它的线程。)

 

* 题外话: 在Java和其他一些开源项目中,如Mina,会经常碰到类似的应用,想了解它,还是自己编个小程序,做测试吧,相信会有所收获!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值