java中的并发:线程通信 博客分类: java基础 java线程并发生产着消费者
目录
1.wait(),notify(),notifyAll()
2.生产者消费者模式
3.多生产多消费模式
1.wait(),notify(),notifyAll()
Object类为我们定义了线程通信的方法,如wait(),notify()等,这些方式是本地的而且是final的.
1.1wait()
1)调用wait()方法,能让当前线程阻塞并交出此对象的monitor,然后进入等待状态直到其他线程调用此对象的notify()或notifyAll()方法.当前的线程必须拥有此对象的monitor,也就是说wait()方法需要在Synchronized域内使用.
2)wait()和sleep()的区别
wait | sleep | |
所属类 | Thread | Object |
对象锁 | 释放 | 不释放 |
使用环境 | synchronized域内 | 任意环境 |
唤醒方式 | 通过notify或notifyAll唤醒 | 休眠到指定时间自动唤醒 |
1.2notify(),notifyAll()
调notify()方法能够唤醒一个正在等待这个对象的monitor的某一个线程,notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程.同样的,这两个方法需要在Synchronized域内使用,也不会释放锁,.
2.生产者消费者模式
生产者消费者模式是经典的线程通信模式,其主旨为两个线程交替对一个共享资源进行操作,并相互进行通信.例:
public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); } } // 资源类 class Resource { private boolean flag = false; private int count = 0; public synchronized void put() { if (flag) { try { wait(); } catch (InterruptedException e) { } } count++; System.out.println("生产者行为..." + count); flag = true; notify(); } public synchronized void get() { if (!flag) { try { wait(); } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count); flag = false; notify(); } } // 生产者线程 class Producer implements Runnable { private Resource r; public Producer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.put(); } } } // 消费者线程 class Consumer implements Runnable { private Resource r; public Consumer(Resource r) { this.r = r; } @Override public void run() { while (true) { r.get(); } } }
运行结果:生产者消费者交替执行,每次对应的行为计数(count)相同.
3.多生产多消费模式
现实生活中长对应多个生产者和多个消费者,因此我们可以增加线程来实现.在上一节的main()函数内增加多个线程,例:
public class ThreadDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); } }
运行结果:生产者消费者执行过程中偶尔会执行多次同一行为:
生产者行为...41133
消费者行为......41133
消费者行为......41133
我们已经使用了synchronized了,究竟是哪里出错了呢?让我们重新来看消费者的行为代码:
public synchronized void get() { if (!flag) {// 1 try { wait();// 2 } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count);// 3 flag = false;// 4 notify();// 5 }
假设消费者C1调用了get()方法并得到了当前对象的锁,执行代码第1行进行if判断>>>如果if语句判断为true,C1进入等待状态并释放当前对象锁>>>当其他线程调用了notify()唤醒C1后,C1继续执行之前的任务,执行代码第345行.问题就是在这里出现的,因为C1再次执行时没有进行flag标志位的判断而继续执行,而如果此时flag标志位不满足条件时,打印的数据就是错的.为了使线程再被唤醒后能再次对标志位进行判断,我们可以将if语句改为while语句.例:
public synchronized void get() { while (!flag) {// 1 try { wait();// 2 } catch (InterruptedException e) { } } System.out.println("消费者行为......" + count);// 3 flag = false;// 4 notify();// 5 }
修改后再次运行,程序运行一会后阻塞了,又是为什么呢?再次分析修改后的代码:
消费者C1正常执行后调用了notify()方法>>>由于notify()方法只能唤醒某一个等待线程,如果唤醒的是另一个消费者C2,C2获得执行权>>>由于flag标志位没变,因此C2也会进入等待状态.但是如果这是最后一个非等待状态的线程,那么所有线程都会处于wait()状态从而阻塞.也就是说,这次是notify()引起的问题,如果notify()唤醒的是本方线程,那么是没有意义的,因此我们可以使用notifyAll()唤醒所有线程,从而达到唤醒对方线程的目的.再次修改后的代码:
public synchronized void put() { while (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println(Thread.currentThread().getName() + "生产者行为..." + count); flag = true; notifyAll(); } public synchronized void get() { while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + "消费者行为......" + count); flag = false; notifyAll(); }
运行结果:多生产者之间和多消费者之间是随机执行的,但每一个生产者和一个消费者对应并执行同样的计数行为.
总结:在多生产多消费模式中,需通过while判断和notifyAll()唤醒所有线程,以实现通信功能.notifyAll()虽然达到了唤醒对方的目的,但同时也唤醒了所有本方的线程,因此也是影响性能的,在后面的高级并发对象中我们会解决这样的问题.
推荐阅读
-
java中的并发:同步 博客分类: java基础 java线程并发同步
-
java中的并发:进程和线程 博客分类: java基础 java进程线程并发
-
java中的并发:线程通信 博客分类: java基础 java线程并发生产着消费者
-
Java分布式应用学习笔记05多线程下的并发同步器----后篇 博客分类: 分布式集群 分布式集群线程调度多线程同步器
-
Java分布式应用学习笔记05多线程下的并发同步器----前篇 博客分类: 分布式集群 分布式集群并发包线程调度器多线程
-
Java并发编程的艺术-----Java并发编程基础(线程间通信)
-
Java并发编程的艺术-----Java并发编程基础(线程间通信)