通常情况下,应用程序会在一个线程中生产数据,在另一个线程中使用它们。常见的例子是在一个线程中获取网络上的数据,在另一个线程中把这些数据展现给用户。这种模式成为生产者/消费者模式,在面向对象的编程中,开发者用算法来实现要花上几个小时。现在就来介绍一些简化生产者消费者模式实现的现成类。
1、更智能的队列
虽然已有现成的类并能用更少的代码实现该功能,但许多Java开发者仍然选择使用LinkedList以及同步块实现队列功能。下面使用标准的LinkedList实现线程安全队列的代码。
public class ThreadSafeQueue{
private LinkedList<String> mList=new LinkedList<String>();
private final Object mLock=new Object();
public void offer(String value){
synchronized(mLock){
mList.offer(value);
mLock.notifyAll();
}
}
public synchronized String poll(){
synchronized(mLock){
while(mList.isEmpty){
try{
mLock.wait();
}catch(InterruptedException e){
//简介起见忽略异常处理
}
}
return mList.poll();
}
}
}
虽然这段代码是正确的,并有可能得满分,但实现和测试这样一段代码只是浪费时间。实际上,所有前面的代码都可以用下面的一行代码代替。
LinkedBlockingQueue<String> blockingQueue=new LinkedBlockingQueue<String>();
上面的代码能像前面的例子一样提供相同类型的阻塞队列,甚至能提供额外的线程安全操作。
2、更智能的锁
Java 提供的synchronized关键字允许开发者创建线程安全的方法和代码块。synchronized关键字容易使用,也容易滥用,对性能造成负面影响。当需要区分读数据和写数据时,synchronized关键字并不是最有效的。幸好,java.util.concurrent.locks包中的工具类对这种情况提供了很好的支持。
public class ReadWriteLockDemo{
private final ReentrantReadWriteLock mLock;
private String mName;
private int mAge;
private String mAddress;
public ReadWriteLockDemo(){
mLock=new ReentrantReadWriteLock();
}
public void setPersonData(String name,int age,String address){
ReentrantReadWriteLock,WriteLock writeLock=mLock.writeLock();
try{
writeLock.lock();
mName=name;
mAge=age;
mAddress=address;
}finally{
writeLock.unlock();
}
}
public String getName(){
ReentrantReadWriteLock.ReadLock readLock=mLock.readLock();
try{
readLock.lock();
return mName;
}finally{
readLock.unlock();
}
}
}
上面的代码展示了在什么地方使用ReentrantReadWriteLock,它允许多个并发线程对数据进行只读访问,并确保同一时间只有一个线程写入相同的数据。
在代码 中使用synchronized关键字仍然是处理所问题的有效方法,但无论何种情况下,都要考虑ReentrantReadWriteLock是否是更有效的解决方案。