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

来源:互联网 发布:黑猫seo技术网 编辑:程序博客网 时间:2024/06/05 07:17

线程间通信

为什么要进行线程间通信?有一个很经典的例子就是生产者-消费者案例(简化后):必须是生产者生产一个,消费者消费一个,没有生产不能消费,没有消费不能生产。生产者和消费者分别是两个不同的线程,那这两个线程在执行的时候必须相互通信才能按照要求执行下去:生产者生产前先看看消费者消费完了没?消费完了再生产,否则不生产,生产好了之后,告诉消费者你来消费;同样的,消费者消费前先看看你生产了没?生产了就消费,否则不消费,消费好了之后,告诉生产者你来生产。这就是典型的线程间通信问题。
首先要明白的是:线程通信的基础是线程同步,如果线程间连同步都不用的话,就更没有通信的必要了。线程同步是对互斥资源(说互斥不太严谨,是需要控制访问的资源,不一定是互斥)的同步访问,而线程间通信是线程间运行进度的相互影响。
线程间通信即线程间相互影响,相互影响无非是操作资源影响和运行进度影响,资源影响由同步和进度影响共同造成,进度影响即是对线程进行手动的状态间转换。
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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic void run(){while(q.n<10)q.set();}}class Customer2 implements Runnable{Queue2 q;public Customer2(Queue2 q){this.q = q;}@Overridepublic void run(){while(q.n<10)q.get();}}

1 0