------<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类,实现对指定线程的操作。