java多线程之并发协作生产者消费者设计模式
两个线程一个生产者个一个消费者
需求情景
两个线程,一个负责生产,一个负责消费,生产者生产一个,消费者消费一个
涉及问题
同步问题:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用标记或加锁机制
wait() / nofity() 方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
代码实现(共三个类和一个main方法的测试类)
Resource.java /** * Created by yuandl on 2016-10-11./** * 资源 */ public class Resource { /*资源序号*/ private int number = 0; /*资源标记*/ private boolean flag = false; /** * 生产资源 */ public synchronized void create() { if (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费; try { wait();//让生产线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } number++;//生产一个 System.out.println(Thread.currentThread().getName() + "生产者------------" + number); flag = true;//将资源标记为已经生产 notify();//唤醒在等待操作资源的线程(队列) } /** * 消费资源 */ public synchronized void destroy() { if (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费者****" + number); flag = false; notify(); } }
Producer.java
/** * Created by yuandl on 2016-10-11. * /** * 生产者 */ public class Producer implements Runnable { private Resource resource; public Producer(Resource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } resource.create(); } } }
Consumer.java
/** * 消费者 */ public class Consumer implements Runnable { private Resource resource; public Consumer(Resource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } resource.destroy(); } } }
ProducerConsumerTest.java
/** * Created by yuandl on 2016-10-11. */ public class ProducerConsumerTest { public static void main(String args[]) { Resource resource = new Resource(); new Thread(new Producer(resource)).start();//生产者线程 new Thread(new Consumer(resource)).start();//消费者线程 } }
打印结果
Thread-0生产者------------1 Thread-1消费者****1 Thread-0生产者------------2 Thread-1消费者****2 Thread-0生产者------------3 Thread-1消费者****3 Thread-0生产者------------4 Thread-1消费者****4 Thread-0生产者------------5 Thread-1消费者****5 Thread-0生产者------------6 Thread-1消费者****6 Thread-0生产者------------7 Thread-1消费者****7 Thread-0生产者------------8 Thread-1消费者****8 Thread-0生产者------------9 Thread-1消费者****9 Thread-0生产者------------10 Thread-1消费者****10
以上打印结果可以看出没有任何问题
多个线程,多个生产者和多个消费者的问题
需求情景
四个线程,两个个负责生产,两个个负责消费,生产者生产一个,消费者消费一个
涉及问题
notifyAll()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的所有线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
再次测试代码
ProducerConsumerTest.java /** * Created by yuandl on 2016-10-11. */ public class ProducerConsumerTest { public static void main(String args[]) { Resource resource = new Resource(); new Thread(new Consumer(resource)).start();//生产者线程 new Thread(new Consumer(resource)).start();//生产者线程 new Thread(new Producer(resource)).start();//消费者线程 new Thread(new Producer(resource)).start();//消费者线程 } }
运行结果
Thread-0生产者------------100 Thread-3消费者****100 Thread-0生产者------------101 Thread-3消费者****101 Thread-2消费者****101 Thread-1生产者------------102 Thread-3消费者****102 Thread-0生产者------------103 Thread-2消费者****103 Thread-1生产者------------104 Thread-3消费者****104 Thread-1生产者------------105 Thread-0生产者------------106 Thread-2消费者****106 Thread-1生产者------------107 Thread-3消费者****107 Thread-0生产者------------108 Thread-2消费者****108 Thread-0生产者------------109 Thread-2消费者****109 Thread-1生产者------------110 Thread-3消费者****110
通过以上打印结果发现问题
101生产了一次,消费了两次
105生产了,而没有消费
原因分析
当两个线程同时操作生产者生产或者消费者消费时,如果有生产者或者的两个线程都wait()时,再次notify(),由于其中一个线程已经改变了标记而另外一个线程再次往下直接执行的时候没有判断标记而导致的。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
解决方案
while判断标记,解决了线程获取执行权后,是否要运行!也就是每次wait()后再notify()时先再次判断标记
代码改进(Resource中的if->while)
Resource.java
/** * Created by yuandl on 2016-10-11./** * 资源 */ public class Resource { /*资源序号*/ private int number = 0; /*资源标记*/ private boolean flag = false; /** * 生产资源 */ public synchronized void create() { while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费; try { wait();//让生产线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } number++;//生产一个 System.out.println(Thread.currentThread().getName() + "生产者------------" + number); flag = true;//将资源标记为已经生产 notify();//唤醒在等待操作资源的线程(队列) } /** * 消费资源 */ public synchronized void destroy() { while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费者****" + number); flag = false; notify(); } }
再次发现问题
打印到某个值比如生产完74,程序运行卡死了,好像锁死了一样。
原因分析
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致”死锁”。
解决方案
notifyAll解决了本方线程一定会唤醒对方线程的问题。
最后代码改进(Resource中的notify()->notifyAll())
Resource.java
/** * Created by yuandl on 2016-10-11./** * 资源 */ public class Resource { /*资源序号*/ private int number = 0; /*资源标记*/ private boolean flag = false; /** * 生产资源 */ public synchronized void create() { while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费; try { wait();//让生产线程等待 } catch (InterruptedException e) { e.printStackTrace(); } } number++;//生产一个 System.out.println(Thread.currentThread().getName() + "生产者------------" + number); flag = true;//将资源标记为已经生产 notifyAll();//唤醒在等待操作资源的线程(队列) } /** * 消费资源 */ public synchronized void destroy() { while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费者****" + number); flag = false; notifyAll(); } }
运行结果
Thread-0生产者------------412 Thread-2消费者****412 Thread-0生产者------------413 Thread-3消费者****413 Thread-1生产者------------414 Thread-2消费者****414 Thread-1生产者------------415 Thread-2消费者****415 Thread-0生产者------------416 Thread-3消费者****416 Thread-1生产者------------417 Thread-3消费者****417 Thread-0生产者------------418 Thread-2消费者****418 Thread-0生产者------------419 Thread-3消费者****419 Thread-1生产者------------420 Thread-2消费者****420
以上就大功告成了,没有任何问题

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Soalan dan penyelesaian yang sering ditanya untuk percetakan tiket kertas terma depan dalam pembangunan front-end, percetakan tiket adalah keperluan umum. Walau bagaimanapun, banyak pemaju sedang melaksanakan ...

Tidak ada gaji mutlak untuk pemaju Python dan JavaScript, bergantung kepada kemahiran dan keperluan industri. 1. Python boleh dibayar lebih banyak dalam sains data dan pembelajaran mesin. 2. JavaScript mempunyai permintaan yang besar dalam perkembangan depan dan stack penuh, dan gajinya juga cukup besar. 3. Faktor mempengaruhi termasuk pengalaman, lokasi geografi, saiz syarikat dan kemahiran khusus.

Bagaimana cara menggabungkan elemen array dengan ID yang sama ke dalam satu objek dalam JavaScript? Semasa memproses data, kita sering menghadapi keperluan untuk mempunyai id yang sama ...

JavaScript adalah asas kepada pembangunan web moden, dan fungsi utamanya termasuk pengaturcaraan yang didorong oleh peristiwa, penjanaan kandungan dinamik dan pengaturcaraan tak segerak. 1) Pengaturcaraan yang didorong oleh peristiwa membolehkan laman web berubah secara dinamik mengikut operasi pengguna. 2) Penjanaan kandungan dinamik membolehkan kandungan halaman diselaraskan mengikut syarat. 3) Pengaturcaraan Asynchronous memastikan bahawa antara muka pengguna tidak disekat. JavaScript digunakan secara meluas dalam interaksi web, aplikasi satu halaman dan pembangunan sisi pelayan, sangat meningkatkan fleksibiliti pengalaman pengguna dan pembangunan silang platform.

Perbincangan mendalam mengenai punca-punca utama perbezaan dalam output konsol.log. Artikel ini akan menganalisis perbezaan hasil output fungsi Console.log dalam sekeping kod dan menerangkan sebab -sebab di belakangnya. � ...

Perbincangan mengenai realisasi kesan animasi tatal dan elemen Parallax dalam artikel ini akan meneroka bagaimana untuk mencapai yang serupa dengan laman web rasmi Shiseido (https://www.shiseido.co.jp/sb/wonderland/) ... ...

Sebaik sahaja anda telah menguasai tutorial TypeScript peringkat kemasukan, anda harus dapat menulis kod anda sendiri dalam IDE yang menyokong TypeScript dan menyusunnya ke dalam JavaScript. Tutorial ini akan menyelam ke dalam pelbagai jenis data dalam TypeScript. JavaScript mempunyai tujuh jenis data: null, undefined, boolean, nombor, rentetan, simbol (diperkenalkan oleh ES6) dan objek. Typescript mentakrifkan lebih banyak jenis atas dasar ini, dan tutorial ini akan meliputi semuanya secara terperinci. Jenis data null Seperti JavaScript, Null dalam TypeScript

JavaScript boleh dijalankan di PowerPoint, dan boleh dilaksanakan dengan memanggil fail JavaScript luaran atau membenamkan fail HTML melalui VBA. 1. Untuk menggunakan VBA untuk memanggil fail JavaScript, anda perlu mendayakan makro dan mempunyai pengetahuan pengaturcaraan VBA. 2. Benamkan fail HTML yang mengandungi JavaScript, yang mudah dan mudah digunakan tetapi tertakluk kepada sekatan keselamatan. Kelebihan termasuk fungsi lanjutan dan fleksibiliti, sementara kelemahan melibatkan keselamatan, keserasian dan kerumitan. Dalam praktiknya, perhatian harus dibayar kepada keselamatan, keserasian, prestasi dan pengalaman pengguna.
