Java基础--并发编程基础(3)

线程间通信

为什么要进行线程间通信?有一个很经典的例子就是生产者-消费者案例(简化后):必须是生产者生产一个,消费者消费一个,没有生产不能消费,没有消费不能生产。生产者和消费者分别是两个不同的线程,那这两个线程在执行的时候必须相互通信才能按照要求执行下去:生产者生产前先看看消费者消费完了没?消费完了再生产,否则不生产,生产好了之后,告诉消费者你来消费;同样的,消费者消费前先看看你生产了没?生产了就消费,否则不消费,消费好了之后,告诉生产者你来生产。这就是典型的线程间通信问题。
首先要明白的是:线程通信的基础是线程同步,如果线程间连同步都不用的话,就更没有通信的必要了。线程同步是对互斥资源(说互斥不太严谨,是需要控制访问的资源,不一定是互斥)的同步访问,而线程间通信是线程间运行进度的相互影响。
线程间通信即线程间相互影响,相互影响无非是操作资源影响和运行进度影响,资源影响由同步和进度影响共同造成,进度影响即是对线程进行手动的状态间转换。
Java中线程间通信是使用wait()、notify()、notifyAll()这三个方法完成的(除了这基本的线程间通信的方式,后续博文还会探讨其他线程间通信的方式)。
wait()方法是Object类的不可重写方法,那也就是说每个对象都有这个方法哦。调用wait方法后,持有当前对象监视器的线程交出监视器,并且挂起,等待被唤醒(当然也有可能你没有唤醒它,它却由于假唤醒醒了)
notify()方法是Object类的不可重写方法,也是每个类都有这个方法。调用notify方法后,唤醒一个等待持有该对象监视器的任意(随机的哦)一个线程
notifyAll()方法是Object类的不可重写方法,每个类都有。调用这个方法后,唤醒所有等待持有该对象监视器的线程
未使用线程间通信的生产者-消费者:
public class ProducerCustomerWithoutCommunication {

	public static void main(String[] args) {
		Queue q = new Queue();
		q.n = 0;
		Thread t1 = new Thread(new Producer(q));
		t1.start();
		Thread t2 = new Thread(new Customer(q));
		t2.start();
		
		/*
		 * 想要实现的效果是:生产一个拿走一个,生产一个拿走一个,即生产完一个如果没有拿走的话,不再生产,如果生产者还没生产出来,则不拿
		 */
		//使用线程间通讯之前
//		Set: 1
//		Set: 2
//		Set: 3
//		Get: 3
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Get: 4
//		Set: 4
//		Set: 5
//		Get: 4
		/*
		 * 没有达到效果,因为连续生产了三次后才开始消费,然后连续消费了
		 * 更过分的是生产者还没有生产完(显然已经生产了4)4,消费者已经拿到4了
		 */
	}

}
class Queue{
	int n;  
	/*
	 * 这里加同步是为了保证每次要么只能生产要么只能消费,不能这里生产没完呢就开始消费也不能消费没完成就开始生产
	 * 但是,根据输出:4还没生产完就被消费了,并不是这里的问题,而是其他地方的问题?发现了吗?
	 */
	public synchronized void get(){
		System.out.println("Get: "+n);
	}
	public synchronized void set(int n){
		this.n = n;
		System.out.println("Set: "+n);
	}
}
class Producer implements Runnable{
	Queue q;
	Producer(Queue q){
		this.q = q;
	}
	@Override
	public void run(){
		/*
		 * 线程不能被杀死,只能通过设置标志位的方式使其终结,即让run方法返回后这个线程就终结了
		 */
		while(q.n<5&&q.n>-5){
			int n = q.n;
			n++;
			q.set(n);
		}
	}
}
class Customer implements Runnable{
	Queue q;
	public Customer(Queue q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<5&&q.n>-5){
			q.get();
		}
	}
}
使用了线程间通信的生产者-消费者:
public class ProducerCustomerWithCommunication {

	public static void main(String[] args) {
		Queue2 q = new Queue2();
		Thread t1 = new Thread(new Producer2(q));
		Thread t2 = new Thread(new Customer2(q));
		t1.start();
		t2.start();
		
//		运行结果:
//		Set..1
//		Get..1
//		Set..2
//		Get..2
//		Set..3
//		Get..3
//		Set..4
//		Get..4
//		Set..5
//		Get..5
//		Set..6
//		Get..6
//		Set..7
//		Get..7
//		Set..8
//		Get..8
//		Set..9
//		Get..9
//		Set..10
//		Get..10

	}

}

class Queue2{
	int n = 0;
	boolean isProduced = false;
	public synchronized void set(){
		if(isProduced)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		n++;
		System.out.println("Set.."+n);
		isProduced = true;
		notify();
	}
	public synchronized void get(){
		if(!isProduced)
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("Get.."+n);
		isProduced = false;
		notify();
	}
}
class Producer2 implements Runnable{
	Queue2 q;
	public Producer2(Queue2 q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<10)
			q.set();
	}
}
class Customer2 implements Runnable{
	Queue2 q;
	public Customer2(Queue2 q){
		this.q = q;
	}
	@Override
	public void run(){
		while(q.n<10)
			q.get();
	}
}

你可能感兴趣的:(Java基础--并发编程基础(3))