个人建议还是把我之前写的几个版本生产者-消费者都了解一下,有利于加深对并发编程的理解
1.synchronzied版本-多生产者-多消费者-模型
2.Lock版本-多生产者-多消费者-模型
3.多生产-多消费-总结(生产一组,消费一组)
这个代码里面会有几个比较核心问题放在上边,在这里给大家解答一下:
使用阻塞队列的方式的好处?
1.实现生产者和消费者进行解耦,作为开发者无需关心复杂的线程之间的通信(await,signal,signalAll等监视器方法),让开发者更加专注业务;
2.阻塞队列封装了线程之间的通信;
flag字段为什么要加生volatile关键字?
1.阻塞队列的方式,是没有Lock锁,也就是我们的 pro(生产方法) consu(消费方法)都是线程不安全的,也就无法通过锁的同步机制,实现共享变量的可见行;
2.volatile主要是对保证当一个线程修改了共享变量的时候,其他线程能够立刻能看见
为什么使用多个原子类?
1.资源类的共享方法没有使用锁的同步机制,所以必须要使用原子类(CAS)来保证多个线程操作count(计数器)的正确性;
**上代码 **
资源类
static class Resource {
//这个flag控制 控制生产者消费者消息的运行;
//为什么要加volatile,这是为了保证线程的可见行;
//当有一个线程操作flag时,对于其他线程都是可见的;
private volatile boolean flag = true;
//为什么要用原子类,这是因为资源类的方法都没有使用Lock锁,这也就是说明 pro consu 生产,消费方法不是线程安全,
// 当多个线程更新共享变量,会出现线程安全问题;
private AtomicInteger count = new AtomicInteger();
//使用阻塞队列来实现了 生产者和消费者的解耦合,也无需关心复杂的await,signal,signalAll等线程之间的通信;
private BlockingQueue<String> queue;
public Resource(BlockingQueue<String> queue) {
this.queue = queue;
System.out.println(queue.getClass().getName() + " 阻塞队列创建");
}
public void stop() {
this.flag = false;
System.out.println("BlockQueueProdConsu stop ....");
}
public void pro() throws Exception {
int data;
boolean res;
while (flag) {
//保证不会重复生产数据
data = count.incrementAndGet();
res = queue.offer(data + "", 2, TimeUnit.SECONDS);
if (res) {
System.out.println(Thread.currentThread().getName() + "生产 数据" + data);
} else {
System.out.println(Thread.currentThread().getName() + "生产 数据失败" + data);
}
Thread.sleep(1000);
}
}
public void consu() throws Exception {
String res = null;
while (flag) {
res = queue.poll(2, TimeUnit.SECONDS);
if (res == null) {
System.out.println(Thread.currentThread().getName() + "消费 数据 失败");
System.out.println(Thread.currentThread().getName() + "消费者 退出");
break;
}
System.out.println(Thread.currentThread().getName() + "消费 数据 " + res);
}
}
}
测试方法
public static void main(String[] args) throws InterruptedException {
Resource resource = new Resource(new ArrayBlockingQueue<>(3));
new Thread(() -> {
try { resource.pro(); } catch (Exception e) { }
},"生产者1").start();
new Thread(() -> {
try { resource.pro(); } catch (Exception e) { }
},"生产者2").start();
new Thread(() -> {
try { resource.consu(); } catch (Exception e) { }
},"消费者1").start();
new Thread(() -> {
try { resource.consu(); } catch (Exception e) { }
},"消费者2").start();
Thread.sleep(5000);
resource.stop();
System.out.println("主程序结束");
}