欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java中的并发:线程通信 博客分类: java基础 java线程并发生产着消费者 

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