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狀態。因此不會有異常發生。

相關文章
相關標籤/搜索