不能停止

          運動,游玩,學習,我的愛不能停止

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            5 隨筆 :: 3 文章 :: 8 評論 :: 0 Trackbacks

          線程
          1.程序如果只有后臺進程而沒有一個前臺進程,那么整個java程序就會結束。用setDaemon(true)可以把線程設為后臺線程,普通創(chuàng)建的線程都是前臺線程。
          2.一個線程類繼承Thread類,這個子類實現(xiàn)run方法,run方法處理的事物的處理。啟動線程用start方法。[thread類本身的run方法是空的,所以需要子類去實現(xiàn),start方法也是thread類里繼承過來的,由于java的多態(tài)性,所以start方法會啟動子類的run方法]
          3.join()方法可以合并線程,如果不帶參數(shù)表示永久合并,如果帶參數(shù)表示線程合并多少豪秒后又分開執(zhí)行

          class ThreadDemo
          {
              public static void main(String [] args)
              {
                  Thread tt = new TestThread();
                  //tt.setDaemon(true);把線程tt置為后臺線程
                  tt.start();//這個方法會自動去調用tt的run()
                  int index = 0;
                  while(true)
                  {
                       if(index++ == 100)//執(zhí)行100次后合并線程
                                try{tt.join(10000);//把tt線程合并到主線程10秒,如果這里是沒有參數(shù),那么就永遠執(zhí)行run這                                          //個線程,因為這個線程是個死循環(huán),下面的語句就執(zhí)行不到了,哈哈}                      catch(Exception e){}
                       System.out.println("main()"+Thread.currentThread().gerName()//得到當前執(zhí)行線程的名字);
                  }
              }
          }

          class TestThread extends Thread
          {
             public void run()
             {
                while(true)
                {
                       System.out.println("run()"+Thread.currentThread().gerName()//得到當前執(zhí)行線程的名字);

                }
             }
          }

          以上說了如何用繼承Thread類來創(chuàng)建線程,還有一種方法是類來實現(xiàn)Runnable接口來創(chuàng)建線程
          class TestThread extends Thread 這個就要改成 class TestThread implements Runnable
          而main中的Thread tt = new TestThread(); 就要改成 Thread tt = new Thread(new TestThread());這里接受的類型是Runnable型的

          那么就出現(xiàn)了一個問題,既然兩種方法都可以創(chuàng)建線程,那么有什么區(qū)別呢?我們來看一個鐵路模擬售票系統(tǒng):
          有100張票,我們啟動4個線程來賣他們:
          class ThreadDemo
          {
              public static void main(String [] args)
              {
                /* 如果用這種寫法,你會發(fā)現(xiàn)實際是啟動了4個線程,然后4個線程都有各自的100張票,各買各的,所以這樣是錯的。
                 new TestThread();
                 new TestThread();
                 new TestThread();
                 new TestThread(); */

                /*如果是這種寫法,從輸出結果中我們發(fā)現(xiàn)無論start了多少次,實際還是一個線程,所以這樣也是錯的。
                 TestThread tt = new TestThread();
                  tt.start();
                  tt.start();
                  tt.start();
                  tt.start();
                */
                
              }
          }

          class TestThread extends Thread
          {
             int tickets = 100;
             public void run()
             {
                while(true)
                {
                       if(tickets>0)
                       System.out.println(Thread.currentThread().gerName()+"is saling ticket "+tickets--);

                }
             }
          }
          這種情況下我們就使用runnable了。請看:
          class ThreadDemo
          {
              public static void main(String [] args)
              {
                  TestThread tt = new TestThread();
                  new Thread(tt).start();//四個線程調用同一個線程對象
                  new Thread(tt).start();
                  new Thread(tt).start();
                  new Thread(tt).start();
                
              }
          }

          class TestThread implements Runnable
          {
             int tickets = 100;
             public void run()
             {
                while(true)
                {
                       if(tickets>0)
                       System.out.println(Thread.currentThread().gerName()+"is saling ticket "+tickets--);

                }
             }
          }


          總結:使用runnable接口創(chuàng)建多線程比繼承thread要靈活。他適合多個相同的程序代碼的線程去處理同一資源的情況。

          多線程的應用:
          1.網(wǎng)絡聊天工具開發(fā)
            一個線程負責發(fā)消息,一個負責收消息。兩者互不干擾,如果是單線程,那么就可能前面在等待,而后面就接收不了消息。
          2.大量數(shù)據(jù)庫記錄的復制。
            如果一個復制要花3天時間,那么當你中途覺得沒有必要,要停止的時候,你發(fā)現(xiàn)你不能讓他停下來。而多線程的情況,一個
            現(xiàn)成負責拷貝,是個無限循環(huán)
            while(bStop)
            {get data
             copy data}
            另一個現(xiàn)成監(jiān)督是否有鍵盤按下,也就是把原本為true的變量bstop置為false。   bStop = false;當另一線程監(jiān)視到變量  變化時會停止程序
          3.www服務器為每一個來訪者創(chuàng)建一個線程


          線程安全問題:
          我們在上面做的鐵路模擬獸票系統(tǒng)有個問題:線程安全問題!
          假如當tickets==5的時,系統(tǒng)正好正好要執(zhí)行tickets--,而還美意執(zhí)行這個的時候,cpu被切換到另一個線程。那么tickets仍然是5,所以很坑5這張票打印了2次,另一種情況:當票為1的時候系統(tǒng)剛好要打印出1,而這個時候cpu馬上切換到了其他線程,它發(fā)現(xiàn)票還是1,所以通過了判斷大于0,所以打印出了票1。而這個時候票--為0,cpu切換到了剛才的線程,由于剛才的線程在執(zhí)行打印,所以把票0給打印出來,這些都造成了線程的安全問題

          所以我們要把if語句塊作為一個原子,放到synchronized()里面。synchronized()里面必須有參數(shù),比如
          String str = new String("");
          然后把if語句塊放到synchronized(str){ if... }里面  這樣就實現(xiàn)了同步,避免了線程的不安全。
          另外,我們如果希望一個方法是線程安全的,那么我們可以直接在方法名前寫上synchronized關鍵字,比如:
          public synchronized void sale(){......} //實際上這種情況下synchronized使用的同步對象是this
          注意:str的定義必須放到run方法之外,這個對象應該是全局的,這樣才能作為鎖旗標?。ㄏ到y(tǒng)會自動對這個對象進行標識)

          線程執(zhí)行的內部機制:
          new Thread(tt).start();
          執(zhí)行start并沒有馬上去執(zhí)行線程的run方法,而是再向下繼續(xù)執(zhí)行一會,因為cpu還沒有這么快就切換到線程上,要想讓他馬上切換,可以在start后寫上:try(Thread.sleep(1);//哪怕是一秒,他也會切換)catch(Exception e){}

          如果把代碼塊的同步用synchronized(this),那么代碼塊會和有synchronized的方法同步!

          1:31 為止 5

          類似下面的代碼在多線程調用它的時候也要注意線程安全問題:
          public void push(char c)
          {
            data[index] = c;
            index++;
          }

          另外有一場景[線程間通信問題]:生產(chǎn)者不停地產(chǎn)生一組數(shù)據(jù),消費者不停地去取數(shù)據(jù),數(shù)據(jù)都在緩存中,
          class Producer implements Runnable
          {
                 Q q;
           public Producer(Q q)
                  {
                      this.q = q;
                  }
                 public void run()
                 {
                       int i = 0;
                       while(true)
                       {
                           if(i==0)
                           {q.name = "zhangsan";
              try{Thread.sleep(1);}catch(Exception e){}//用sleep來模擬cpu切換的現(xiàn)象
                            q.sex = "male";
                            }
                            else
                           {q.name = "lisi";
                            q.sex = "female";
                            }
               i = (i+1)%2;  //使i在0和1之間切換
                          

                       }
                 }
          }
          class Customer implements Runnable
          {
                Q q;
           public Customer(Q q)
                  {
                      this.q = q;
                  }
                 public void run()
                 {
                      
                       while(true)
                       {
            System.out.println(q.name);
            System.out.println(":"+q.sex);

                       }
                 }
          }
          class Q
          {
                String name = "unknow";
                String sex = "unknow";
          }

          //run class
          class ThreadCommunation
          {
                public static void main(String [] args)
           {
            Q q = new Q();
            new Thread(new Prodecer(q)).start();
            new Thread(new Customer(q)).start();
           }

          }
          運行后我們會發(fā)現(xiàn)名字和性別并沒有對應起來,這是因為線程沒有同步的問題,要解決這個問題,只需要在兩個類的while里
          分別加上synchronized(q){}語句塊,因為他們都使用同一個對象q,所以用q作為同步監(jiān)視器
            上面的代碼還不是很完善,因為有當消費者取得同步監(jiān)視器卻發(fā)現(xiàn)緩沖區(qū)根本沒有數(shù)據(jù)的情況,那怎么辦?或者當生產(chǎn)者在

          取得同步監(jiān)視器開始生產(chǎn)數(shù)據(jù)的時候又取得了同步監(jiān)視器開始放數(shù)據(jù),這樣就會把原先的數(shù)據(jù)覆蓋!我們使用了wait和notify

          對程序修改如下:
          class Producer implements Runnable
          {
                 Q q;
           public Producer(Q q)
                  {
                      this.q = q;
                  }
                 public void run()
                 {
                       int i = 0;
                       while(true)
                       {
            synchronized(q)
            {
             if(q.bFull)//如果緩沖區(qū)是滿的 有數(shù)據(jù)的 那么生產(chǎn)者線程應該wait等待消費者來取數(shù)據(jù)
             try{q.wait();}catch(Exception e){}
                           if(i==0)
                           {q.name = "zhangsan";
              try{Thread.sleep(1);}catch(Exception e){}//用sleep來模擬cpu切換的現(xiàn)象
                            q.sex = "male";
                            }
                            else
                           {q.name = "lisi";
                            q.sex = "female";
                            }
              q.bFull = true;
              q.notify();//通知消費者有數(shù)據(jù)要他來取數(shù)據(jù)
             }
               i = (i+1)%2;  //使i在0和1之間切換
                          
            
                       }
                 }
          }
          class Customer implements Runnable
          {
                Q q;
           public Customer(Q q)
                  {
                      this.q = q;
                  }
                 public void run()
                 {
                      
                       while(true)
                       {
            synchronized(q)
            {
            if(!q.bFull)  //緩沖區(qū)為空的時候交出同步監(jiān)視器開始等待
                try{q.wait();}catch(Exception e){}
            System.out.println(q.name);
            System.out.println(":"+q.sex);
               q.bFull = false;  //取走數(shù)據(jù)后緩沖區(qū)為空
            
               q.notify(); //通知生產(chǎn)者線程開始執(zhí)行,這個notify與Producer中的wait對應

            }
                       }
                 }
          }
          class Q
          {
                String name = "unknow";
                String sex = "unknow";
                boolean bFull = false;
          }

          //run class
          class ThreadCommunation
          {
                public static void main(String [] args)
           {
            Q q = new Q();
            new Thread(new Prodecer(q)).start();
            new Thread(new Customer(q)).start();
           }

          }

          注意:wait和notify方法必須是synchronized的監(jiān)視器對象的方法,既如果有
          synchronized(q),那么就應該q.wait();q.notify(); 如果不寫對象,他會默認是this對象
          而有可能導致運行時錯誤[編譯沒有錯誤]

          任何對象都有wait,notify,notifyAll方法,
          wait:告訴當前線程放棄監(jiān)視器并進入睡眠狀態(tài)直到其他線程進入同一監(jiān)視器并調用notify為止;
          notify:喚醒同一對象監(jiān)視器中調用wait的第一個線程。
                 用于類似飯館有一個空位后通知所有等候就餐的顧客中的第一位可以入座的情況。
          notifyAll:喚醒同一對象監(jiān)視器中調用wait的所有線程。具有最高優(yōu)先級的線程首先被喚醒并執(zhí)行。
                    用于類似某個不定期的培訓班終于招生滿額后,通知所有的學員都來上課的情況。


          實際上上面的代碼有些亂,這是因為程序的結構設計不夠合理,應該把數(shù)據(jù)的放和取放到q類中,然后在
          這兩個方法名前加上關鍵字synchronized,類似
          public synchronized void put(String name,String sex)
          {
            if(bFull)
               try{wait();}catch(Exception e){}
            this.name = name;
             try{Thread.sleep(1);}catch(Exception e){}
            this.sex = sex;
            bFull = true;
            notify();
          }
          我們給別人提供的類是線程安全的,類似上面的代碼。別人在使用這個類的時候就不需要考慮線程安全問題,反之,就需要在

          外面處理線程安全問題

          控制線程的生命周期:
          class ThreadLife
          {
           public static void main(String [] args)
           {
            ThreadTest tt = new ThreadTest();
            new Thread(tt).start();
            for(int i=0;i<100;i++)
            {
             if(i==50)
              tt.stopMe();
             System.out.println("main() is runing");
            }
           }
          }

          class ThreadTest implements Runnable
          {
           private boolean bStop = false;
           public void stopMe()
           {
            bStop = true;
           }
           public void run()
           {
            while(!bStop)
            {
             System.out.println(Thread.currentThread().getName()+"is running");
            }
           }
          }

           

           

          posted on 2005-10-27 13:31 快樂的射手 閱讀(501) 評論(0)  編輯  收藏 所屬分類: java

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導航:
          博客園   IT新聞   Chat2DB   C++博客   博問  
           
          主站蜘蛛池模板: 张家口市| 乌审旗| 馆陶县| 林西县| 安多县| 永昌县| 潼南县| 兴宁市| 莲花县| 稷山县| 耿马| 禹州市| 仙游县| 肇庆市| 兴安县| 广河县| 赤峰市| 宝鸡市| 收藏| 饶阳县| 天水市| 孙吴县| 朝阳市| 龙泉市| 定南县| 上虞市| 宝丰县| 尉氏县| 西盟| 镇安县| 通江县| 马鞍山市| 松桃| 宁蒗| 弋阳县| 祁连县| 筠连县| 密云县| 公安县| 博罗县| 陆川县|