多线程间通信
Input------->变量库------->Output
多个线程操作同一个资源,但是操作的动作不同
eg.同时输入输出会存在控制权转移导致输入一半就输出的问题
class Res{
String name;
String sex;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r=r;
}
public void run(){
int x=0;
while(true){
if(x==0){
r.name="Mike";
r.sex="female";
}
else{
r.name="Lily";
r.sex="male";
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r=r;
}
public void run(){
while(true){
System.out.println(r.name+"___"+r.sex);
}
}
}
class InputOutputDemo{
public static void main(String[] args){
Res r=new Res();
Input in=new Input(r);
Output out=new Output(r);
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
利用同步synchronized(唯一对象)解决控制权问题
class Res{
String name;
String sex;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r=r;
}
public void run(){
int x=0;
while(true){
synchronized(r){ //共享数据
if(x==0){
r.name="Mike";
r.sex="female";
}
else{
r.name="Lily";
r.sex="male";
}
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r=r;
}
public void run(){
while(true){
synchronized(r){ //共享数据
System.out.println(r.name+"___"+r.sex);
}
}
}
}
将运行结果改为输入一条立即输出,依次循环——等待与唤醒
wait()等待 notify()唤醒 notifyAll唤醒全部
**因为是对持有锁的线程操作,因此只能用于同步之中,且必须标识锁——r.wait()
定义在Object类中是因为只有同一个锁上的wait线程,可以被同一个锁notify
锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中
class Res{
String name;
String sex;
boolean flag=false;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r=r;
}
public void run(){
int x=0;
while(true){
synchronized(r){
if(r.flag){
try{r.wait();}catch(Exception e){}
}
if(x==0){
r.name="Mike";
r.sex="female";
}
else{
r.name="Lily";
r.sex="male";
}
r.flag=true;
r.notify();
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r=r;
}
public void run(){
while(true){
synchronized(r){
if(!r.flag){
try{r.wait();}catch(Exception e){} //必须抛出异常,且必须标识锁
}
System.out.println(r.name+"___"+r.sex);
r.flag=false;
r.notify();
}
}
}
}
对代码进行优化
class Res{
private String name; //权限私有化
private String sex;
private boolean flag=false;
public synchronized void set(String name,String sex){ //提供对外接口,注意同步数据
if(flag){
try{this.wait();}catch(Exception e){}
}
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronized void out(){
if(!flag){
try{this.wait();}catch(Exception e){}
}
System.out.println(name+"___"+sex);
flag=false;
this.notify();
}
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r=r;
}
public void run(){
int x=0;
while(true){
if(x==0){
r.set("Mike","Male");
}
else{
r.set("Lily","Female");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r=r;
}
public void run(){
while(true){
r.out();
}
}
}
class InputOutputDemo{
public static void main(String[] args){
Res r=new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
练习:消费者与生产者通信
Ver 1.0
//存在问题:t1 t2唤醒后不再进行判断,可能生产出统一编号
class ProducerConsumerDemo{
public static void main(String[] args){
Resource r=new Resource();
Producer p=new Producer(r);
Consumer c=new Consumer(r);
Thread t1=new Thread(p);
Thread t2=new Thread(p);
Thread t3=new Thread(c);
Thread t4=new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name){
if(flag){ //第一次判断flag为false,不执行wait(),第二次为true,执行wait().
try{
this.wait();
}
catch(Exception e){
}
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
this.notify();
}
public synchronized void out(){
if(!flag){ //第一次执行到这里的时候flag=true,不执行wait().
try{
this.wait();
}
catch(Exception e){
}
}
System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
flag=false;
this.notify();
}
}
class Producer implements Runnable{
private Resource res;
Producer(Resource res){
this.res=res;
}
public void run(){
while(true){
res.set("+goods+");
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res=res;
}
public void run(){
while(true){
res.out();
}
}
}
Ver 2.0
存在多个消费者与生产者时
将if语句改为while,单次判断变为多次判断。同时改为notifyAll避免全部wait
class Resource{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name){
while(flag){
try{
this.wait();
}
catch(Exception e){
}
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
this.notifyAll();
}
public synchronized void out(){
while(!flag){
try{
this.wait();
}
catch(Exception e){
}
}
System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
flag=false;
this.notifyAll();
}
}
总结
对于多个生产者和消费者,使用while进行判断,让被唤醒的线程再次判断flag
只用notify容易出现只唤醒本方线程的情况,导致所有线程都wait。
notifyAll能够唤醒对方线程。
Java 5.0以后将同步synchronized替换为Lock操作
将wait notify等操作替换为Condition对象
该对象可以对Lock锁进行操作
import java.util.concurrent.locks.*;
class Resource{
private String name;
private int count=1;
private boolean flag=false;
private Lock lock=new ReentrantLock();
private Condition con_pro=lock.newCondition();
private Condition con_con=lock.newCondition();
public void set(String name)throws InterruptedException{
lock.lock();
try{
while(flag){
con_pro.await(); //只唤醒对方
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
con_con.signal();
}
finally{ //释放锁的动作一定要执行
lock.unlock();
}
}
public void out()throws InterruptedException{
lock.lock();
try{
while(!flag){
con_con.await();
}
System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
flag=false;
con_pro.signal();
}
finally{
lock.unlock();
}
}
}
class Producer implements Runnable{
private Resource res;
Producer(Resource res){
this.res=res;
}
public void run(){
while(true){
try{
res.set("+goods+");
}
catch(InterruptedException e){
}
}
}
}
class Consumer implements Runnable{
private Resource res;
Consumer(Resource res){
this.res=res;
}
public void run(){
while(true){
try{
res.out();
}
catch(InterruptedException e){
}
}
}
}