并发编程中有一个典型的问题是生产者-消费者(Producer-Consumer)问题。我们有一个数据缓冲区,一个或者多个生产者将数据存入这个缓冲区,一个或者多个数据消费者将数据从缓冲区中取走。
使用synchronized关键字
只是用synchronized关键字会有诸多限制。如果缓冲区是满的,生产者不能再放入新的数据,如果缓冲区是空的,消费者不能取到数据。
对于这些场景java在Object类中提供了wait()、notify()、notifyAll()方法
/**
* @Desciption 缓冲区
*
* @Author SuFH
*/
public class EventStorage {
private int maxSize;
private LinkedList<Long> storage;
public EventStorage(int max) {
//初始化缓冲区
this.maxSize = max;
this.storage = new LinkedList<Long>();
}
public synchronized void set() {
while (storage.size() == maxSize) {
try {
wait();//挂起当前同步代码块
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
storage.add(System.currentTimeMillis());
System.out.printf("Set: %d\n", storage.size());
// notify();//唤醒一个正在等待该对象的线程,但需要确定不会造成死锁
notifyAll();// 唤醒所有正在等待该对象的线程。
}
public synchronized void get() {
while (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.printf("Get: %d : %s\n", storage.size(), storage.poll());
notifyAll();
}
}
执行:
public class Test {
public static void main(String[] args) {
EventStorage storage = new EventStorage(10);
Producer producer = new Producer(storage);
Thread thread = new Thread(producer);
Consumer consumer = new Consumer(storage);
Thread thread2 = new Thread(consumer);
thread.start();
thread2.start();
}
}
class Producer implements Runnable {
private EventStorage storage;
public Producer(EventStorage storage) {
super();
this.storage = storage;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
this.storage.set();
System.out.println("Producer---"+i);
}
}
}
class Consumer implements Runnable {
private EventStorage storage;
public Consumer(EventStorage storage) {
super();
this.storage = storage;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
this.storage.get();
System.out.println("Consumer---"+i);
}
}
}
数据存储的EventStorage类中的set()和get()。
set()方法检查存储列表storage是否还有空间,如果满了,调用wait()挂起线程释放控制这个代码块的对象,等待空余空间出现。该代码块的唤醒却决于其他线程调用当前对象的notifyAll()唤醒等待线程来竞争锁。
get()与之不同之处在于条件是判别空间中无数据则挂起。
使用ReadWriteLock接口
ReadWriteLock与它的唯一实现类ReentrantReadWriteLock。这个类有两个锁分别是读操作锁与写操作锁。其中读操作锁允许多个线程同时访问,但是写操作锁至允许一个线程进行访问,并且在进行写操作时,不能进行读操作
/**
* @Desciption 缓冲区
*
* @Author SuFH
*/
public class PricesInfo {
private double prices1;
private double prices2;
private ReadWriteLock lock;
public PricesInfo() {
this.prices1 = 0.1;
this.prices2 = 0.2;
this.lock = new ReentrantReadWriteLock();
}
/**读操作
* @return prices1
*/
public double getPrices1() {
lock.readLock().lock();
double value=prices1;
lock.readLock().unlock();
return value;
}
/**读操作
* @return prices2
*/
public double getPrices2() {
lock.readLock().lock();
double value=prices2;
lock.readLock().unlock();
return value;
}
/**写操作
* @param prices1
* @param prices2
*/
public void setPrices(double prices1, double prices2) {
lock.writeLock().lock();
this.prices1 = prices1;
this.prices2 = prices2;
lock.writeLock().unlock();
}
}
执行:
public class Test {
public static void main(String[] args) {
PricesInfo info = new PricesInfo();
Read[] reads = new Read[5];
Thread[] thread = new Thread[5];
for (int i = 0; i < 5; i++) {
reads[i] = new Read(info);
thread[i]=new Thread(reads[i]);
}
Writer writer=new Writer(info);
Thread thread2=new Thread(writer);
for (int i = 0; i < 5; i++) {
thread[i].start();
}
thread2.start();
}
}
class Read implements Runnable {
private PricesInfo info;
public Read(PricesInfo info) {
// TODO Auto-generated constructor stub
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.printf("%s Price 1: %f\n", Thread.currentThread().getName(), this.info.getPrices1());
System.out.printf("%s Price 2: %f\n", Thread.currentThread().getName(), this.info.getPrices2());
}
}
}
class Writer implements Runnable {
private PricesInfo info;
public Writer(PricesInfo info) {
this.info = info;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 3; i++) {
System.out.println("Writer: Attempt to modify the price.");
this.info.setPrices(Math.random()*10,Math.random()*5);
System.out.println("Writer: Prices have been modfied.");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}