------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
多线程可以让我们同时共享一个资源,但如果在共享这个资源时需要彼此之间的联系怎么做呢?
经典实例:生产者与消费者。
问题描述,生产者每生产一个消费者就要取走一个,同时进行。
首先java为我们提供了一套等待唤醒机制,让线程与线程之间产生了联系。线程是分五个状态的:创建;运行;阻塞;冻结;消亡。java提供了几个针对状态的方法
wait()方法让线程进入冻结状态,让出cpu,让出锁;
notify()方法唤醒进入冻结状态的线程,notifyAll()是唤醒所有的线程。
针对问题,我们可以对生产者设定一个开关,如果资源为0就允许其生产并在生产完之后将开关关上,不会在有生产者生产并唤醒其他线程也就是消费者线程,当然在这之上同样要用到同步,为了保证第一个生产者进去之时,其他生产者会被拒之门外,对于消费者同理。
public static void main(String[] args) {
//
tongbu i=new tongbu();//创建抽取出来的同步方法对象
hjw j=new hjw(i);//创建实现Runnable接口的对象
bb k=new bb(i);
Thread xc1 =new Thread(j);//创建线程
Thread xc2 =new Thread(k);
xc1.setName("生产者");
xc2.setName("消费者");
xc1.start();
xc2.start();
}
}
class tongbu{//将两个同步方法抽取出来
private String name;
private int count=1;
boolean flag=false;//定义一个bool类型来作用等待唤醒机制
public synchronized void set(String name){//生产者的同步函数
if(flag){//已存在就睡眠
try{
wait();
}
catch(Exception e){}
}
//否则 运作代码
this.name=name+"---"+count++;
System.out.println(Thread.currentThread().getName()+this.name);
flag=true;//改变bool值
this.notify();//唤醒线程
}
public synchronized void get(){
if(!flag){//进入休眠
try{
wait();
}
catch(Exception e){}
}
//被生产者唤醒
System.out.println(Thread.currentThread().getName()+"---"+this.name);
flag=false;
this.notify();//唤醒线程
}
}
class hjw implements Runnable{
private tongbu x ;
hjw(tongbu x){//传回tongbu对象
this.x=x;
}
public void run(){//重写run方法
while(true){
x.set("饼干");
}
}
}
class bb implements Runnable{
private tongbu x ;
bb(tongbu x){//传回tongbu对象
this.x=x;
}
public void run(){//重写run方法
while(true){
x.get();
}
}
}
这只是对于生产者和消费者只有一个线程,如果这边都有多个线程呢?绝不是多创建几个线程那么简单,因为要notify方法是唤醒最先休眠的那个线程,也就是说转到后面会出,生产一个,消费两次,生产多个只消费其中一个。
那么如何避免呢?采用while循环替代if循环,每当线程解冻之后,重新开始循环而非接着向下执行,这样就避免了多次执行,但问题是依据notify()的特性还是会让所有线程都陷入等待,我需要唤醒对方线程,所有这个时候就要用到notifyAll()了,唤醒所有线程。
public static void main(String[] args) {
//
tongbu i=new tongbu();//创建抽取出来的同步方法对象
hjw j=new hjw(i);//创建实现Runnable接口的对象
bb k=new bb(i);
Thread xc1 =new Thread(j);//创建线程
Thread xc2 =new Thread(k);
xc1.setName("生产者");
xc2.setName("消费者");
xc1.start();
xc2.start();
}
}
class tongbu{//将两个同步方法抽取出来
private String name;
private int count=1;
boolean flag=false;//定义一个bool类型来作用等待唤醒机制
public synchronized void set(String name){//生产者的同步函数
while(flag){//已存在就睡眠
try{
wait();
}
catch(Exception e){}
}
//否则 运作代码
this.name=name+"---"+count++;
System.out.println(Thread.currentThread().getName()+this.name);
flag=true;//改变bool值
this.notifyAll();//唤醒线程
}
public synchronized void get(){
while(!flag){//进入休眠
try{
wait();
}
catch(Exception e){}
}
//被生产者唤醒
System.out.println(Thread.currentThread().getName()+"---"+this.name);
flag=false;
this.notifyAll();//唤醒线程
}
}
class hjw implements Runnable{
private tongbu x ;
hjw(tongbu x){//传回tongbu对象
this.x=x;
}
public void run(){//重写run方法
while(true){
x.set("饼干");
}
}
}
class bb implements Runnable{
private tongbu x ;
bb(tongbu x){//传回tongbu对象
this.x=x;
}
public void run(){//重写run方法
while(true){
x.get();
}
}
}
事实上,java5.0版本提供更完善的解决方案,针对如何唤醒指定线程。在java.lang.util.concurrent.locks中,提供了方法将锁显示化用以取代同步synchronized,lock()开锁,unlock()关锁,关锁这一特点犹如关闭资源就是无论怎样都要执行,因为前面有异常需要处理,所以关锁这一操作放在了finally{}中;而对于Object中wait()方法和notify(),notifyAll()方法分别用condition的await(),signal()和signalAll()取代,在lock中有一个new condition的方法,所以可以通过创建多个condition类,实现对指定线程的操作。