不能停止

          運(yùn)動(dòng),游玩,學(xué)習(xí),我的愛不能停止

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

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

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

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

                }
             }
          }

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

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

                /*如果是這種寫法,從輸出結(jié)果中我們發(fā)現(xiàn)無論start了多少次,實(shí)際還是一個(gè)線程,所以這樣也是錯(cuò)的。
                 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();//四個(gè)線程調(diào)用同一個(gè)線程對象
                  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--);

                }
             }
          }


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

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


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

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

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

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

          1:31 為止 5

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

          另外有一場景[線程間通信問題]:生產(chǎn)者不停地產(chǎn)生一組數(shù)據(jù),消費(fèi)者不停地去取數(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();
           }

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

          取得同步監(jiān)視器開始生產(chǎn)數(shù)據(jù)的時(shí)候又取得了同步監(jiān)視器開始放數(shù)據(jù),這樣就會(huì)把原先的數(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)者線程應(yīng)該wait等待消費(fèi)者來取數(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();//通知消費(fèi)者有數(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ū)為空的時(shí)候交出同步監(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í)行,這個(gè)notify與Producer中的wait對應(yīng)

            }
                       }
                 }
          }
          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),那么就應(yīng)該q.wait();q.notify(); 如果不寫對象,他會(huì)默認(rèn)是this對象
          而有可能導(dǎo)致運(yùn)行時(shí)錯(cuò)誤[編譯沒有錯(cuò)誤]

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


          實(shí)際上上面的代碼有些亂,這是因?yàn)槌绦虻慕Y(jié)構(gòu)設(shè)計(jì)不夠合理,應(yīng)該把數(shù)據(jù)的放和取放到q類中,然后在
          這兩個(gè)方法名前加上關(guān)鍵字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();
          }
          我們給別人提供的類是線程安全的,類似上面的代碼。別人在使用這個(gè)類的時(shí)候就不需要考慮線程安全問題,反之,就需要在

          外面處理線程安全問題

          控制線程的生命周期:
          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 快樂的射手 閱讀(496) 評論(0)  編輯  收藏 所屬分類: java

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 阳朔县| 德钦县| 五指山市| 澄江县| 华蓥市| 安龙县| 沧州市| 娄底市| 柯坪县| 龙岩市| 石屏县| 泽普县| 奈曼旗| 沁源县| 怀远县| 嵊泗县| 霍山县| 土默特左旗| 泊头市| 金塔县| 海丰县| 滨州市| 华安县| 上犹县| 东辽县| 馆陶县| 元谋县| 桐柏县| 内丘县| 东方市| 怀仁县| 吉安市| 通海县| 梅河口市| 安多县| 和龙市| 正阳县| 西充县| 洛扎县| 青海省| 平乡县|