线程间通信问题学习

线程间通信涉及的方法

    多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。

    等待/唤醒机制涉及的方法:
    1. wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
    2. notify():唤醒线程池中的一个线程(任何一个都有可能)。
    3. notifyAll():唤醒线程池中的所有线程。


    P.S.
    1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
    2、必须要明确到底操作的是哪个锁上的线程!
    3、wait和sleep区别?
         1)wait可以指定时间也可以不指定。sleep必须指定时间。
         2)在同步中时,对CPU的执行权和锁的处理不同。
         wait:释放执行权,释放锁。
         sleep:释放执行权,不释放锁。


    为什么操作线程的方法wait、notify、notifyAll定义在了object类中,因为这些方法是监视器的方法,监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定在object类中。


    生产者-消费者问题:
class Resource{
       private String name ;
       private String sex ;
       private boolean flag = false;

       public synchronized void set(String name,String sex){
             if(flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             this.name = name;
             this.sex = sex;
             flag = true ;
             this.notify();
      }
      
      public synchronized void out(){
             if(!flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             System. out.println(name + "..." + sex);
             flag = false ;
             this.notify();
     } 
}

//输入
class Input implements Runnable{
      Resource r;
      Input(Resource r){
             this.r = r;
      }

      public void run(){
             int x = 0;
             while(true ){
                   if(x == 0){
                         r.set( "mike","男" );
                  } else{
                         r.set( "lili","女" );
                  }
                  x = (x + 1)%2;
            }
      }
}

//输出
class Output implements Runnable{
      Resource r;

      Output(Resource r){
             this.r = r;
      }

       public void run(){
             while(true ){
                   r.out();
            }
      }
}

class ResourceDemo {
       public static void main(String[] args){
             //创建资源
            Resource r = new Resource();
             //创建任务
            Input in = new Input(r);
            Output out = new Output(r);
             //创建线程,执行路径
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
             //开启线程
            t1.start();
            t2.start();
      }
}

多生产者-多消费者问题:

class Resource{
       private String name ;
       private int count = 1;
       private boolean flag = false;

       public synchronized void set(String name){
             if(flag )
                   try{
                        wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             this.name = name + count;
             count++;
             System.out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
             flag = true ;
            notify();
       }

       public synchronized void out(){
            if(!flag )
                   try{
                        wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
            flag = false ;
            notify();
            System.out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
      }
}

class Producer implements Runnable{
      private Resource r ;
      Producer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.set( "烤鸭");
            }
      }
}

class Consumer implements Runnable{
      private Resource r ;
      Consumer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.out();
            }
      }
}
 
class ProducerConsumerDemo {
       public static void main(String[] args){
            Resource r = new Resource();
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);

            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
      }
} 

原因分析:
    得到以上结果的过程分析如下:
    1. 线程Thread-0获取到CPU执行权及锁,生产了烤鸭3298,将flag设置为true。然后,Thread-0又重新获取到CPU执行权,由于flag为true,故执行wait方法,阻塞。Thread-1接着获取到CPU执行权,由于flag为true,故执行wait方法,也阻塞。
 

    2. 线程Thread-3获取到CPU执行权及锁,消费了烤鸭3298,将flag设置为false。然后,线程Thread-0被唤醒,但是并没有获取到锁,而是线程Thread-3接着获取到CPU执行权及锁,然而此时flag为false,所以Thread-3阻塞。下面线程Thread-2接着获取到CPU执行权及锁,然而此时flag为false,所以Thread-2也阻塞。
线程间通信问题学习_第1张图片 

    3. 线程Thread-0获取到CPU执行权及锁,不需要if语句判断,直接生产烤鸭3299,然后又唤醒线程Thread-1获取到CPU执行权及锁,不需要if语句判断,直接生产烤鸭3300。从而造成了烤鸭3299还没有被消费,就直接生产了烤鸭3300的情况。

    由于if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断标记,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
    notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。

    P.S.
    while判断标记+notify会导致死锁的示例:
    如果将上面的代码中的if判断标记修改成wile判断标记,就会出现死锁的现象,前2步与原来是一致的。第3步如下:
    3. 线程Thread-0获取到CPU执行权及锁,通过了while语句判断,直接生产烤鸭3299,将flag设置为true。然后又唤醒线程Thread-1获取到CPU执行权及锁,没有通过while语句判断,阻塞。线程Thread-0又获取到CPU执行权及锁,通不过while语句判断,也阻塞,此时Thread-0、1、2、3都阻塞,故死锁。

你可能感兴趣的:(线程间通信问题学习)