线程间通信问题学习

来源:互联网 发布:利用excel制作软件 编辑:程序博客网 时间:2024/05/16 04:48

线程间通信涉及的方法

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

    等待/唤醒机制涉及的方法:
    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也阻塞。
 

    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都阻塞,故死锁。

0 0
原创粉丝点击