在一个程序中,往往会通过多个线程协同来共同完成一项任务,线程间必然需要进行信息的传递,也即是进程间的通信,我们用生产者和消费者的例子来具体分析:
对于生产者和消费者之间的关系,他们都是针对同一资源的操作,分析其中存在的线程同步和互斥关系:
多个生产者之间的同步,多个消费者之间的同步,生产者和消费者之间的互斥(针对同一个资源)。
相应的我们如何去处理这些问题?我们通过生产者和消费者代码边实现边分析:
/**
* 线程通信客户端
*
*/
public class ThreadCommunication {
public static void main(String[] args) {
User user = new User();
new Thread(new Producer(user)).start();
new Thread(new Consumer(user)).start();
}
}
/**
* 生成者线程
*
*/
class Producer implements Runnable{
User user;
/**
* 构造方法
* @param user
*/
public Producer(User user){
this.user = user;
}
public void run(){
int i = 0;
while(true){
if(i==0){
//操作资源对象
user.put("zhang", "male");
}else{
//操作资源对象
user.put("liu", "famal");
}
i = (i+1)%2;
}
}
}
/**
* 消费者线程
*/
class Consumer implements Runnable{
User user;
/**
* 构造方法
* @param user
*/
public Consumer(User user){
this.user = user;
}
public void run(){
while(true){
//获取资源对象
user.get();
}
}
}
/**
* 资源缓存区(资源,用来监视的对象)
* 资源操作方法(put生产资源,get消费资源,供其他方法调用,并且是线程安全的)
* 通过对bFull判断,通过wait()和notify()实现线程间的等待和通知。
*/
class User{
String name="";
String sex="";
boolean bFull = false;
public synchronized void put(String name,String sex){
if(bFull){
try {
wait(); //进入等待状态,等待另一进程notify()释放资源
this.name = name;
Thread.sleep(1);
this.sex = sex;
bFull = true;
notify(); //通知,唤醒同一对象监视器中调用wait的第一个线程
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!bFull){
try {
wait();
System.out.print(name);
System.out.println(":" + sex);
bFull = false;
notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
说明:
1、生产者和消费者都是对资源在进行操作,一个生产方法put(),一个消费方法get(),都需要是线程同步的(通过synchronized都监视User对象)
2、wait()等待,告诉当前线程放弃监视器并进入睡眠状态直到其他线程进入同一监视器并调用notify为止。
3、notify()通知,唤醒同一对象监视器中调用wait的第一个线程。
4、notifyAll()通知全部,唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先非唤醒并执行。
于此我们来看下线程的一个生命周期图:
说明:
stop(),方法已过时
suspend()挂起,方法已过时
resume()释放,方法已过时,只是将线程挂起,并不会释放锁(同步监视器),所以可能导致死锁的出现。
总结:
在多线程的编程中,线程间的通信经常会涉及到的,如何进行通信并保证线程安全是很重要的。上面就是我们通过生产者和消费者的实例,通过共享同一个对象,来实现他们之间的交互处理。