Java多线程学习(六)

    线程是操做系统中独立的个体,若是不通过处理就不能成为一个总体。使线程进行通讯后,系统之间的交互性会更强大,在大大提升CPU利用率的同时还可以对个线程任务在处理过程当中进行有效的把控和监督。java

  1. 等待/通知机制this

    首先看一下一个不用等待/通知机制实现线程间通讯的例子,使用sleep结合while(true)死循环来实现.操作系统

    public class MyList {
        private List list = new ArrayList();
       public void add(){
            list.add("  ");
        }
        public int size() {
             return list.size();
        }
     }
     public class ThreadA extends Thread {
         MyList myList;
         public ThreadA(MyList list){
              this.myList= list;
         }
         public void run(){
             for(int i= 0; i < 10; i ++){
                myList.add();
               ThreadA.sleep(1000);
             }
         }
     }
      public class ThreadB extends Thread {
         MyList myList;
         public ThreadB(MyList list){
              this.myList= list;
         }
         public void run(){
             while(true){
                 if(myList.size() == 5){
                     throw new InterruptedException();
                 }
              }
          }
    }
    public class Test {
        public static void main(String [] args){
            MyList list = new MyList();
           ThreadA a = new ThreadA(list);
           ThreadB b = new ThreadB(list);
           a.start();
           b.start();
        }
    }
    运行结果:在size等于5的时候,ThreadB退出

    ThreadB中不停的经过while语句轮询机制来检测某一个条件,这样极大的浪费了CPU资源。线程

  2. 由wait/notify方法实现的等待/通知机制code

    ①wait方法的做用是使当前执行代码的线程进行等待,wait方法时Object类的方法。执行wait方法即将当前线程置入“预执行队列”中,而且在wait所在的代码处中止执行,直到接到通知或被中断为止。对象

    在调用wait以前,线程必须得到该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait方法,不然会抛出IllegalMonitorStateException异常,是RuntimeException的子类,不须要进行try...catch进行捕捉。队列

    在从wait方法返回前,线程要与其余线程竞争从新得到锁。资源

    ②notify方法用来通知那些可能等待该对象的对象锁的其余线程,若是有多个线程等待,则由线程规划器随机挑选出一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。rem

    在调用notify方法以前线程也必须获取该对象的对象级别锁,不然会抛出异常。同步

    在执行notify方法以后,当前线程不会立刻释放该对象锁,呈wait状态的线程也并不能立刻获取该对象锁,要等到执行notify方法的线程将程序执行完,即退出synchronized代码块后,当前线程才会释放锁,而呈wait状态的线程才能够得到该对象锁。

    当第一个得到了该线程锁的wait线程执行完毕后,它会释放该线程锁,但此时该对象若没有再次发出通知的话,则即使该对象已经空闲,其余线程因为没有获得该对象的通知,还会继续阻塞在wait状态,知道这个对象发出通知。

    ③下面用wait/notify来实现前面的例子

     

    public class MyList {
        private static List list = new ArrayList();
       public static void add(){
            list.add("  ");
        }
        public static int size() {
             return list.size();
        }
     }
     public class ThreadA extends Thread {
         Object lock;
         public ThreadA(Object lock){
              this.lock = lock;
         }
         public void run(){
             synchronized(lock){
                  for(int i= 0; i < 10; i ++){
                      MyList.add();
                      if(MyList.size() == 5){
                             lock.notify();
                       }
                  }
         }
     }
      public class ThreadB extends Thread {
        Object lock;
         public ThreadB(Object lock){
              this.lock = lock;
         }
         public void run(){
             synchronized(lock){
                      if(MyList.size() != 5){
                             lock.wait();
                       }
         }
    }
    public class Test {
        public static void main(String [] args){
           Object lock = new Object();
           ThreadA a = new ThreadA(lock);
           ThreadB b = new ThreadB(lock);
           b.start();
           Thread.sleep(50);
           a.start();
        }
    }
    运行结果:在size等于5的时候,ThreadB接收到通知。

    分析:线程b首先执行。由于size!= 5,因此线程b进入了wait状态,线程a开始执行,知道size==5时,发出通知,线程b进入等待状态,但线程a不会当即释放锁会一直等到size == 10的时候,释放锁。这时线程b恢复运行。

    ④当方法wait()执行完后,锁被自动释放,但执行完notify()方法后,锁却不自动释放。

    ⑤当线程呈wait状态时,调用线程对象的interrupt方法会出现InterruptedException异常。

    ⑥调用notify方法一次只随机通知一个线程进行唤醒。为了唤醒所有线程,可使用notifyAll()方法。

    ⑦wait(long)方法。是等待一段时间内是否有线程对锁进行唤醒,若是超过这个时间则自动唤醒。

    ⑧若通知过早,好比notify方法在wait方法以前执行,则wait线程不会被唤醒。

    ⑨若wait等待的条件发生了变化,则很容易形成程序逻辑的混乱。

    public class Add {
        private String lock;
        public Add(String lock){
             this.lock = lock;
        }
        public void add(){
            synchronized(lock){
                ValueObject.list.add("str");
                lock.notifyAll();
           }
        }
     }
     
    public class Subtract {
        private String lock;
       public Subtract(String lock){
            this.lock = lock;
       }
       public void subtract(){
            synchronized(lock){
                if(ValueObject.list.size() == 0){
                     lock.wait();
                }
                ValueObject.list.remove(0);
           }
       }
    }
    public class ValueObject {
          public static List list = new ArrayList();
    }
    public class ThreadAdd extends Thread {
        private Add add;
        public Thread(Add add){
             this.add = add;
        }
        public void run(){
            add.add();
        }
    }
    public class ThreadSubtract extends Thread {
        private Subtract s;
        public Thread(Subtract sub){
             this.s = sub;
        }
        public void run(){
            s.subtract();
        }
    }
    public class Run {
        public static void main(String [] args){
             String lock =new String("");
            Add add = new Add(lock);
             Subtract sub = new Subtract(lock);
             ThreadSubtract s1 = new ThreadSubtract(sub);
             ThreadSubtract s2 = new ThreadSubtract(sub);
             
             ThreadAdd a = new ThreadAdd(add);
             
             
             s1.start();
             s2.start();
             Thread.sleep(1000);
             a.start();
         }
    }
    运行结果:出现异常IndexOutOfBoundException。

    分析:线程s1和s2先执行,这时size == 0,他们都进入等待状态,而后a线程执行一次add操做后,执行notifyAll唤醒所有线程。这时线程s1和s2都要执行remove操做,因此出现异常。如何解决呢?

    将Subtract中的if改成while就不会再出现异常了
    public class Subtract {
       private String lock;
       public Subtract(String lock){
            this.lock = lock;
       }
       public void subtract(){
            synchronized(lock){
                while(ValueObject.list.size() == 0){
                     lock.wait();
                }
                ValueObject.list.remove(0);
           }
       }
    }

    分析:线程s1和s2都进入了wait状态后,a线程执行add操做bing唤醒所有线程。s1线程先抢到了锁,这时s1线程要进入下一次while循环的条件判断操做,size!= 0,因此,退出while循环,执行remove操做,s1线程释放锁,s2线程开始恢复执行,在进行while循环的条件判断时发现 size == 0,因而又进入了while循环内部,s2线程有进入了wait状态。因此不会有异常发生。

相关文章
相关标签/搜索