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