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()虽然达到了唤醒对方的目的,但同时也唤醒了所有本方的线程,因此也是影响性能的,在后面的高级并发对象中我们会解决这样的问题.

猜你喜欢

转载自xiao1zhao2.iteye.com/blog/2231371