并发编程:线程通信

线程通信概念:

        线程是操作系统中独立的个体,将多个线程(即将多个操作系统中这样的独立个体)整合成一个整体,这就需要线程之间的通信。当线程之间存在通信指挥,系统间地交互性会更加强大,在提高系统利用率的同时还会使开发人员对线程任务在处理过程中进行有效的把控和监督。

wait/notify:

        使用wait/notify方法实现线程间的通信需要注意:① 这两个方法都是由Object类提供,即所有java对象都存在这两种方法;②  wait/notify方法必须配合synchronized关键字使用;③使用wait方法时使线程处于等待状态,释放锁,让出CPU资源,使用notify方法时则唤醒一个正处于等待该资源状态的线程,使该线程继续执行并直到完成或被中断,然后再释放锁,但此线程自身并不立即释放锁。下面给出一段小例子:

public class while加条件判断 {
	private volatile static List<String> list = new ArrayList<String>();
	private void add() {
		list.add("hello");  	
	}
	private int size() {
		return list.size();
	}
	public static void main(String[] args) {
		final while加条件判断 test1 = new while加条件判断();
		final Object obj = new Object();
		Thread thread1 = new Thread(					//测试线程1
				new Runnable() {
					@Override
					public void run() {
							synchronized (obj) {
								try {
									for(int index=0;index<10;index++) {
										test1.add();
										Thread.sleep(500);
										System.out.println("当前线程:"+Thread.currentThread().getName()+"添加了一个元素。。"+test1.size());
										if(test1.size() == 5){
											System.err.println("发出通知");
											obj.notify();				//试图唤醒线程2
											Thread.sleep(3*1000);
										}
									} 
								}catch (InterruptedException e1) {
									System.err.println(e1.getMessage());
								}
							}
					}
				},"thread1");
		Thread thread2 = new Thread(					//测试线程2
				new Runnable() {
					@Override
					public void run() {
						synchronized (obj) {
								if(test1.size() < 5) {
										System.err.println("thread2进入睡眠"); 
										try {
											obj.wait();   //2进入睡眠
										} catch (InterruptedException e) {
											e.printStackTrace();
										}  
								}
								System.err.println("当前线程收到通知:起床吱了一声");
						}
					}
				},"thread2");
		thread2.start();
		try {
			Thread.sleep(1*1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		thread1.start();
	}
}

        锁obj被线程1和线程2拥有,线程2先执行,obj上锁,线程2执行wait方法进入睡眠,释放锁,线程1开始执行,当线程1执行notify方法时唤醒线程2,但是因为notify方法并不立即释放锁,所以线程2被唤醒后依然拿不到资源,只能等待线程1完全执行结束或被中断,释放了obj锁之后才能执行。

非wait/notify机制:

        while加条件判断:设置一个共享变量,线程A以此线程为判断条件,线程B修改该变量值从而达到与线程A之间通信的目的。下面给出一段小例子:

public class while加条件判断 {
	private volatile static List<String> list = new ArrayList<String>();
	private void add() {
		list.add("hello");  	
	}
	private int size() {
		return list.size();
	}
	public static void main(String[] args) {
		final while加条件判断 test1 = new while加条件判断();
		Thread thread1 = new Thread(					//测试线程1
				new Runnable() {
					@Override
					public void run() {
						try {
							for(int index=0;index<10;index++) {
								test1.add();
								System.out.println("当前线程:"+Thread.currentThread().getName()+"添加了一个元素。。");
								Thread.sleep(1000);
							}
						}catch(InterruptedException e) {
							e.printStackTrace();
						}
					}
				},"thread1");
		Thread thread2 = new Thread(					//测试线程2
				new Runnable() {
					@Override
					public void run() {
						while(true) 
							if(test1.size() == 5) {
								System.out.println("当前线程收到通知:"+Thread.currentThread().getName()+" list size = 5 ,停止本线程");
								throw new RuntimeException();
							}
					}
				},"thread2");
		thread1.start();
		thread2.start();
	}
}

        在上面这个例子中,线程thread2以test1这个对象中list的大小为判断条件,当list的大小不为5时一直进行空循环,线程thread1每一秒钟给list增1,当thread1中的run方法执行5次,即thread1给list增加5次时,thread2达到判断条件从而通过抛出一个运行时异常跳出while循环,结束执行。

        这里需要注意的是,list一定要加volatile关键字,或者在while循环体里边做System.out.println();打印,否则不能保证thread2能够读取到准确的数据从而使实验失败。

使用wait/notify模拟Queue:

        BlockingQueue:一个队列,支持阻塞机制的队列。主要方法put和take;put(Object obj),放一个Object进入Queue,若Queue没有足够的空间容纳则阻塞,直到空间足够;take,取走BlockingQueue里边的第一个Object,若BlockingQueue为空则阻塞,直到BlockingQueue有新的数据加入。下面给出一个使用wait/notify模拟BlockingQueue的小例子:

public class 模拟阻塞队列 {
	//使用LinkedList集合来装元素
	private LinkedList<Object> linkList = new LinkedList<Object>();
	//计数器,限制list的容量
	private AtomicInteger count = new AtomicInteger(0);
	//规定上/下限
	private final int maxSize;
	private final int minSize = 0;
	//构造队列
	public 模拟阻塞队列(int size) {
		this.maxSize = size;
	}
	//初始化一个锁
	private final Object lock = new Object();
	//模拟put方法
	public void put(Object e) {
		synchronized (lock) {
			while(count.get() == this.maxSize) {
				try {
					lock.wait();
				}catch(Exception exception) {
					System.err.println(exception.getMessage());
				}
			}
			if(linkList.add(e)) {							//添加Object
				count.incrementAndGet();			//计数器累加
				lock.notify(); 								//唤醒take方法
				System.err.println("新添加:"+e);
			}
			
		}
	}
	//模拟take方法
	public Object take() {
		synchronized (lock) {
			while(count.get() == this.minSize) {
				try {
					lock.wait();
				}catch(Exception exception) {
					System.err.println(exception);
				}
			}
			Object object = linkList.removeFirst();
			if(object != null) {							//取出成功
				count.decrementAndGet();			//计数器累减
				lock.notify(); 								//唤醒put方法
			}
			return object;
		}
	}
	public static void main(String[] args) {
		//初始化队列
		模拟阻塞队列 blockingQueue = new 模拟阻塞队列(5);
		blockingQueue.put("赵");
		blockingQueue.put("钱");
		blockingQueue.put("孙");
		blockingQueue.put("李");
		blockingQueue.put(",");
		System.err.println("当前长度为:"+blockingQueue.count.get());
		
		Thread thread_put = new Thread(new Runnable() {
				@Override
				public void run() {
						blockingQueue.put("周");blockingQueue.put("吴");blockingQueue.put("郑");blockingQueue.put("王");blockingQueue.put("。");
				}
			},"thread_put");
		thread_put.start();
		
		try {
			Thread.sleep(3*1000);
		}catch(Exception exception) {
			System.err.println(exception.getMessage());
		}
		
		Thread thread_take = new Thread(new Runnable() {
			@Override
			public void run() {
				for(int index=1;index<6;index++)
					System.err.println("第"+index+"个移除的Object为"+blockingQueue.take());
			}
		},"thread_take");
		thread_take.start();
	}
}

猜你喜欢

转载自blog.csdn.net/txd2016_5_11/article/details/82989572