Java多线程之并发
来源:互联网 发布:如何评价奚梦瑶 知乎 编辑:程序博客网 时间:2024/05/29 18:41
代码实现(共三个类和一个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生产者------------1Thread-1消费者****1Thread-0生产者------------2Thread-1消费者****2Thread-0生产者------------3Thread-1消费者****3Thread-0生产者------------4Thread-1消费者****4Thread-0生产者------------5Thread-1消费者****5Thread-0生产者------------6Thread-1消费者****6Thread-0生产者------------7Thread-1消费者****7Thread-0生产者------------8Thread-1消费者****8Thread-0生产者------------9Thread-1消费者****9Thread-0生产者------------10Thread-1消费者****10
以上打印结果可以看出没有任何问题
再次测试代码
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生产者------------100Thread-3消费者****100Thread-0生产者------------101Thread-3消费者****101Thread-2消费者****101Thread-1生产者------------102Thread-3消费者****102Thread-0生产者------------103Thread-2消费者****103Thread-1生产者------------104Thread-3消费者****104Thread-1生产者------------105Thread-0生产者------------106Thread-2消费者****106Thread-1生产者------------107Thread-3消费者****107Thread-0生产者------------108Thread-2消费者****108Thread-0生产者------------109Thread-2消费者****109Thread-1生产者------------110Thread-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生产者------------412Thread-2消费者****412Thread-0生产者------------413Thread-3消费者****413Thread-1生产者------------414Thread-2消费者****414Thread-1生产者------------415Thread-2消费者****415Thread-0生产者------------416Thread-3消费者****416Thread-1生产者------------417Thread-3消费者****417Thread-0生产者------------418Thread-2消费者****418Thread-0生产者------------419Thread-3消费者****419Thread-1生产者------------420Thread-2消费者****420
以上就大功告成了
阅读全文
3 0
- Java多线程之并发
- Java多线程之并发锁
- Java多线程之并发锁
- java 多线程并发之-- CountDownLatch
- Java多线程并发之CountDownLatch
- java多线程与并发之java并发编程实践(一)
- java多线程与并发之java并发编程实践(二)
- java多线程与并发之java并发编程实践(三)
- java多线程与并发之java并发编程实践(四)
- java多线程与并发之java并发编程实践(五)
- java多线程与并发之java并发编程实践(六)
- java多线程与并发之java并发编程实践(七)
- java多线程与并发之java并发编程实践(八)
- java多线程与并发之java并发编程实践(九)
- java多线程与并发之java并发编程实践(十)
- java多线程与并发之java并发编程实践
- java多线程与并发之java并发编程实践
- java多线程与并发之java并发编程实践
- 关于 new Promise 和 Promise.resolve()
- HTA是HTML Application的缩写(HTML应用程序)
- Android7.0 获取手机蓝牙MAC地址
- Openlayers之加载高德地图
- Redis和Memcache对比及选择
- Java多线程之并发
- 机器学习距离计算
- Android: INSTALL_FAILED_UPDATE_INCOMPATIBLE错误解决
- Shiro的简单实用
- 【Android7.1.2源码解析系列】Android ADB概览 ---system/core/adb/OVERVIEW.txt
- linux下获取当前目录
- 11.2.0.4集群修改ip、vip、scan过程
- 选择屏幕程序示例
- PID算法的C语言实现四 增量型PID的C语言实现