线程通信的应用:经典例题:生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
首先来用一种单一线程操作共享数据的方法(基于同步锁实现数据安全机制):
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProduct();
}
}, "生产者").start();
new Thread(() -> {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.reduceProduct();
}
}, "消费者").start();
}
}
class Clerk {
private int productNum = 0;
public synchronized void addProduct() {
if (productNum < 20) {
System.out.println(Thread.currentThread().getName() + ":" + ++productNum);
//增加产品后立刻唤醒消费线程
notify();
} else {
//产品为20个的时候就停止生产进入等待状态
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void reduceProduct() {
if (productNum > 0) {
System.out.println(Thread.currentThread().getName() + ":" + productNum--);
//消费一个产品后立刻唤醒生产线程去生产
notify();
} else {
//没有产品时就等待
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
总结分析:本案例中设计2个线程,一个生产线程和一个消费线程,使用的是同步锁机制保证了线程安全问题,但是同时也隐藏着另外一个问题,当多增加几个消费线程的时候,并不能做到并行去消费,所以同步锁机制虽然保证了安全,但是效率会很低,接下来我会用另外一种写法来实现多个消费线程并行操作数据并且能够保证数据安全。
多个消费线程并行操作数据代码如下所示:
import java.util.Date;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ProductTest {
public static void main(String[] args) {
Producer p = new Producer();
Thread p1 = new Thread(p);
p1.start();
Consume c = new Consume();
Thread c1 = new Thread(c);
Thread c2 = new Thread(c);
Thread c3 = new Thread(c);
Thread c4 = new Thread(c);
Thread c5 = new Thread(c);
c1.setName("消费线程1");
c2.setName("消费线程2");
c3.setName("消费线程3");
c4.setName("消费线程4");
c5.setName("消费线程5");
c1.start();
c2.start();
c3.start();
c4.start();
c5.start();
}
}
class Contain {
private static int count=1;
private static int[] datas = new int[20];
private static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock = readWriteLock.writeLock();
private static ThreadLocal<Integer> sizeThreadL = new ThreadLocal<Integer>();
private static AtomicReference<Integer> sizeReference = new AtomicReference<Integer>(0);
public void produceProduct() throws InterruptedException {
writeLock.lock();
sizeThreadL.set(sizeReference.get());
if (sizeReference.get() < datas.length) {
if (sizeReference.compareAndSet(sizeThreadL.get(), sizeReference.get() + 1)) {
datas[sizeThreadL.get()] = count;
System.out.println("生产数据: " + count + " ---时间为:" + new Date());
count++;
}
}
writeLock.unlock();
}
public void consumeProduct() throws InterruptedException {
readLock.lock();
sizeThreadL.set(sizeReference.get());
if (sizeReference.get() > 0) {
if (sizeReference.compareAndSet(sizeThreadL.get(), sizeReference.get() - 1)) {
int data = datas[sizeThreadL.get() - 1];
//这里是安全的
System.out.println(Thread.currentThread().getName() + ": 消费数据: " + data + " ---时间为:"+new Date());
count--;
}
}
readLock.unlock();
}
}
class Producer extends Contain implements Runnable {
@Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
produceProduct();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consume extends Contain implements Runnable {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
consumeProduct();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果如下图所示: