JAVA多线程-线程间通信(一)-等待/通知机制(wait/notify)

线程间通信

    线程与线程之间不是独立的个体,它们彼此之间可以相互通信与协作。

    线程间通信后,系统之间的交互性会更强大,在大大提交CPU利用率的同时,还会使程序要对各线程任务在处理的过程中进行有效的把控和监督。

    学习目录:

    一、使用wait/notify实现线程间的通信

    二、生产者/消费者模式的实现

    三、方法join的使用

    四、ThreadLocal类的使用


    一、使用wait/notify实现线程间的通信

       1.1 不使用等待/通知机制实现线程间通信:

             线程A做i++的操作,线程B利用while(true){if(i==5){to do ...}}轮询去检测 i 的值,这样会非常浪费CPU资源。

             如果轮询时间间隔较短,更浪费CPU资源;如果轮询时间间隔较长,有可能会取不奥想要得到的数据。

             所以,就需要一种机制来减少CPU的资源浪费,而且还可以实现在多个线程间通信,它就是“wait/notify”机制。

       1.2 等待/通知机制的实现】

            方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法是用来将当前线程置入“预执行队列”中,并且在wait()所在的代码处停止执行,知道接到通知或被中断为止。

            wait()方法只能在同步方法中或同步块中调用。如果调用wait()时,没有持有适当的锁,会抛出异常。

            wait()方法执行后,当前线程释放锁,线程与其他线程竞争重新获取锁。


            方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其他线程,对其发出通知notify,并使它重新获取该对象的对象锁。如果有多个线程等待,则有线程规划器随机挑选出一个呈wait状态的线程。

           在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块中。

             小结一下:wait()是使线程停止运行,而notify使停止的线程继续运行。

        简单案例

        代码清单:

<span style="font-size:18px;">package org.jksoft.thread.Volatile;
/**
 * 测试三:使用wait/notify机制的案例
 * 注意:wait/notify都必须在同步代码块中调用。
 * @author mcl
 *
 * 2016-2-20-下午12:01:25
 */
public class Test3 {
	public static void main(String[] args) throws InterruptedException {
		Object lock = new Object();
		MyThread1 t1 = new MyThread1(lock);
		t1.start();
		Thread.sleep(3000);
		MyThread2 t2 = new MyThread2(lock);
		t2.start();
	}
}
class MyThread1 extends Thread{
	private Object lock;
	public MyThread1(Object lock){
		this.lock = lock;
	}
	@Override
	public void run(){
			try {
				synchronized (lock) {
				System.out.println("begin wait   "+System.currentTimeMillis());
				lock.wait();
				System.out.println("end wait     "+System.currentTimeMillis());
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
	}
}
class MyThread2 extends Thread{
	private Object lock;
	public MyThread2(Object lock){
		this.lock = lock;
	}
	@Override
	public void run(){
		synchronized (lock) {
				System.out.println("begin notify "+System.currentTimeMillis());
				lock.notify();
				System.out.println("end notify   "+System.currentTimeMillis());
		}
	}
}</span>
         案例结果:3秒后,唤醒处于wait等待的线程。

 

       Java为每一个Object都实现了wait()和notify()方法,他们必须用于同步代码块内。

        notify()方法可以随机唤醒等待队列中等待同一共享资源的“一个线程”,并使该线程退出等待队列,进入可运行状态,也就是notify()方法仅通知“一个“线程。

       notifyAll()方法可以使所有正在等待队列中等待同一资源的"全部"线程从等待状态中退出,进入可运行状态。

      1.3 方法wait()锁释放与notify()锁不释放。

      1.4 当interrupt方法遇到wait方法

            当线程呈wait状态时,调用线程对象的interrupt()方法会出现异常。

      1.5  方法wait(long)的使用

            带一个参数的wait(long)方法的功能是等待某一个时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。

     1.6 等待wait的条件发生变化

      案例:线程r1,r2负责向list集合中移除一个元素,如果list的size为0,则进入堵塞队列

                 线程a负责向list集合中添加一个元素,然后唤醒所有等待在堵塞队列的所有线程。

      代码清单:

package org.jksoft.thread.Volatile;

import java.util.ArrayList;
import java.util.List;

/**
 * 测试四:使用wait/notify机制的案例,当wait的等待条件发生改变时 注意:wait/notify都必须在同步代码块中调用。
 * 
 * @author mcl
 * 
 *         2016-2-20-下午12:01:25
 */
public class Test4 {
	private static List<String> list = new ArrayList<String>();
	
	public static void main(String[] args) throws InterruptedException {
		String lock = "";
		removeThread r1 = new removeThread(lock);
		r1.start();
		removeThread r2 = new removeThread(lock);
		r2.start();
		Thread.sleep(3000);
		addThread a = new addThread(lock);
		a.start();
	}
	static class addThread extends Thread {
		private String lock;
		public addThread(String lock) {
			this.lock = lock;
		}
		public void run() {
			synchronized (lock) {
				list.add("a");
				lock.notifyAll();
				System.out.println("唤醒所有等待该锁的线程");
			}

		}
	}
	static class removeThread extends Thread {
		private String lock;
		public removeThread(String lock) {
			this.lock = lock;
		}
		public void run() {
			try {
				synchronized (lock) {
					if (list.size() == 0) {
						System.out.println("进入等待唤醒的队列..."
								+ Thread.currentThread().getName());
						lock.wait();
						System.out.println("等待队列的线程被唤醒..."
								+ Thread.currentThread().getName());
					}
					list.remove(0);
				}
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}
}
        运行结果:

 

        运行结果分析:

         刚开始的时候,list的size为0,所以两个r线程进入堵塞队列。等到a线程向list集合中添加一个元素后,唤醒了r1,r2两个线程。两个线程都去继续向下执行,都进行list.remove(0)的运算,那么这样的话,只有一个元素的list是不能移出两个的,所以只能抛出异常。

        问题出现原因:是由于r1,r2两个线程没有重新去验证list的size的条件,导致后续结果的异常。

        解决方案:

            将上诉代码中,

if (list.size() == 0) {
更改为

while (list.size() == 0) { 

         我们再看一下运行结果:这就与我们所期待的结果一致了。

 

 

      

发布了53 篇原创文章 · 获赞 11 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览