把困難踩在腳下

          迎難而上

           

          java 同步鎖(synchronized)

          java中cpu分給每個(gè)線程的時(shí)間片是隨機(jī)的并且在java中好多都是多個(gè)線程共用一個(gè)資源,比如火車賣票,火車票是一定的,但賣火車票的窗口到處都有,每個(gè)窗口就相當(dāng)于一個(gè)線程,這么多的線程共用所有的火車票這個(gè)資源。如果在一個(gè)時(shí)間點(diǎn)上,兩個(gè)線程同時(shí)使用這個(gè)資源,那他們?nèi)〕龅幕疖嚻笔且粯拥模ㄗ惶?hào)一樣),這樣就會(huì)給乘客造成麻煩。比如下面程序:

           

          package com.dr.runnable2;
          class TicketSouce implements Runnable
          {
              
          //票的總數(shù)
              private int ticket=10;
              
          public void run()
              
          {
                  
          for(int i=1;i<50;i++)
                  
          {
                      
          if(ticket>0)
                      
          {
                          
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
                          try {
                              Thread.sleep(
          1000);
                          }
           catch (InterruptedException e) {
                              e.printStackTrace();
                          }

                          System.out.println(Thread.currentThread().getName()
          +"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
                      }

                  }

              }

          }

          public class Test {
              
          public static void main(String args[])
              
          {
                  TicketSouce mt
          =new TicketSouce();
                  
          //基于火車票創(chuàng)建三個(gè)窗口
                  new Thread(mt,"a").start();
                  
          new Thread(mt,"b").start();
                  
          new Thread(mt,"c").start();
              }
           

          }
           

           

          程序的運(yùn)行結(jié)果是:

          ticket

          我們可以看到a號(hào)窗口和和c號(hào)窗口都賣出了7號(hào)票,并且a號(hào)和c號(hào)窗口分別賣出了0號(hào)和-1號(hào)票。造成這種情況的原因是1、a線程和b線程在ticket=7的時(shí)候,a線程取出7號(hào)票以后,ticket還沒來的及減1b線程就取出了ticket此時(shí)ticket還等于7;2、在ticket=1時(shí),b線程取出了1號(hào)票,ticket還沒來的及減1,a、c線程就先后進(jìn)入了if判斷語句,這時(shí)ticket減1了,那么當(dāng)a、c線程取票的時(shí)候就取到了0號(hào)和-1號(hào)票。

          出現(xiàn)了上述情況怎樣改變呢,我們可以這樣做:當(dāng)一個(gè)線程要使用火車票這個(gè)資源時(shí),我們就交給它一把鎖,等它把事情做完后在把鎖給另一個(gè)要用這個(gè)資源的線程。這樣就不會(huì)出現(xiàn)上述情況。 實(shí)現(xiàn)這個(gè)鎖的功能就需要用到synchronized這個(gè)關(guān)鍵字。

          synchronized這個(gè)關(guān)鍵字有兩種用法1、放方法名前形成同步方法;2、放在塊前構(gòu)成同步塊。

          1、使用同步方法將上面的例子該為:

           

          package com.dr.runnable2;
          class TicketSouce implements Runnable
          {
              
          //票的總數(shù)
              private int ticket=10;
              
          public void run()
              
          {
                  
          for(int i=1;i<50;i++)
                  
          {
                      
          try {
                          
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
                          Thread.sleep(1000);
                      }
           catch (InterruptedException e) {
                          e.printStackTrace();
                      }

                      
          this.sale();
                  }

              }

              
          public synchronized void sale()
              
          {
                      
          if(ticket>0)
                      
          {
                          System.out.println(Thread.currentThread().getName()
          +"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
                      }

              }

          }

          public class Test {
              
          public static void main(String args[])
              
          {
                  TicketSouce mt
          =new TicketSouce();
                  
          //基于火車票創(chuàng)建三個(gè)窗口
                  new Thread(mt,"a").start();
                  
          new Thread(mt,"b").start();
                  
          new Thread(mt,"c").start();
              }
           

          }
           

          程序的輸出結(jié)果為:

          ticket1

          2、使用同步塊修改上面的例子:

           

          package com.dr.runnable2;
          class TicketSouce implements Runnable
          {
              
          //票的總數(shù)
              private int ticket=10;
              
          public void run()
              
          {
                  
          for(int i=1;i<50;i++)
                  
          {
                      
          synchronized(this){
                          
          if(ticket>0)
                          
          {
                              
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
                              try {
                                  Thread.sleep(
          1000);
                              }
           catch (InterruptedException e) {
                                  e.printStackTrace();
                              }

                              System.out.println(Thread.currentThread().getName()
          +"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
                          }

                                        }

                  }

              }

          }

          public class Test {
              
          public static void main(String args[])
              
          {
                  TicketSouce mt
          =new TicketSouce();
                  
          //基于火車票創(chuàng)建三個(gè)窗口
                  new Thread(mt,"a").start();
                  
          new Thread(mt,"b").start();
                  
          new Thread(mt,"c").start();
              }
           

          }
           

          程序的輸出結(jié)果:

          ticket2

          上面的情況是正確的,我在調(diào)試程序的時(shí)候把synchronized放錯(cuò)了位置導(dǎo)致了錯(cuò)誤的結(jié)果,我們來看一下錯(cuò)誤的原因:

          程序1:

           

          package com.dr.runnable2;
          class TicketSouce implements Runnable
          {
              
          //票的總數(shù)
              private int ticket=10;
              
          public void run()
              
          {
                  
          for(int i=1;i<50;i++)
                  
          {
                      
          if(ticket>0)
                      
          synchronized(this){
                          
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
                          try {
                              Thread.sleep(
          1000);
                          }
           catch (InterruptedException e) {
                              e.printStackTrace();
                          }

                          System.out.println(Thread.currentThread().getName()
          +"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
                                         }

                  }

              }

          }

          public class Test {
              
          public static void main(String args[])
              
          {
                  TicketSouce mt
          =new TicketSouce();
                  
          //基于火車票創(chuàng)建三個(gè)窗口
                  new Thread(mt,"a").start();
                  
          new Thread(mt,"b").start();
                  
          new Thread(mt,"c").start();
              }
           

          }
           

          程序1的運(yùn)行結(jié)果:

           ticket3

          程序1的輸出結(jié)果竟然出了0號(hào)和-1號(hào)票,原因就是synchronized放錯(cuò)了位置,程序1將synchronized放在了if語句的后面,當(dāng)b線程取出2好票以后,此時(shí)ticket=1,等下一次a、b、c線程來的時(shí)候,ticket=1>0就進(jìn)入if語句體,這時(shí)cpu分給線程的時(shí)間片是先b在c后a這樣就導(dǎo)致了上面的結(jié)果。

          程序2:

           

          package com.dr.runnable2;
          class TicketSouce implements Runnable
          {
              
          //票的總數(shù)
              private int ticket=10;
              
          public void run()
              
          {
                  
          synchronized(this){
                      
          for(int i=1;i<50;i++)
                      
          {
                          
          if(ticket>0)
                          
          {
                              
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
                              try {
                                  Thread.sleep(
          1000);
                              }
           catch (InterruptedException e) {
                                  e.printStackTrace();
                              }

                              System.out.println(Thread.currentThread().getName()
          +"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
                           }

                      }
           

                                     }

                      }

          }

          public class Test {
              
          public static void main(String args[])
              
          {
                  TicketSouce mt
          =new TicketSouce();
                  
          //基于火車票創(chuàng)建三個(gè)窗口
                  new Thread(mt,"a").start();
                  
          new Thread(mt,"b").start();
                  
          new Thread(mt,"c").start();
              }
           

          }
           

          程序2的輸出結(jié)果:

          ticket4

          程序2的輸出結(jié)果看起來并沒有什么錯(cuò)誤,它沒有輸出0和-1號(hào)票,但是它沒有實(shí)現(xiàn)多個(gè)窗口售票的功能,它只有一個(gè)窗口在售票,原因是我們把鎖放錯(cuò)了位置。一旦cpu將時(shí)間片分給一個(gè)線程,那么這個(gè)窗口就必須把所有的票賣完。

          鑒于以上兩種錯(cuò)誤程序?qū)е碌慕Y(jié)果,筆者建議大家使用同步方法這種方法比較方便。
          關(guān)于生產(chǎn)者和消費(fèi)者的問題,請參看“模擬生產(chǎn)零件系統(tǒng)程序”。

          posted on 2010-11-10 13:48 馮魁 閱讀(66051) 評論(11)  編輯  收藏

          評論

          # re: java 同步鎖(synchronized) 2012-10-29 15:46 eagledame

          兄弟 是不是搞錯(cuò)了啊
          a號(hào)窗口 ,售出20號(hào)車票
          a號(hào)窗口 ,售出19號(hào)車票
          a號(hào)窗口 ,售出18號(hào)車票
          a號(hào)窗口 ,售出17號(hào)車票
          a號(hào)窗口 ,售出16號(hào)車票
          a號(hào)窗口 ,售出15號(hào)車票
          a號(hào)窗口 ,售出14號(hào)車票
          a號(hào)窗口 ,售出13號(hào)車票
          a號(hào)窗口 ,售出12號(hào)車票
          a號(hào)窗口 ,售出11號(hào)車票
          d號(hào)窗口 ,售出10號(hào)車票
          d號(hào)窗口 ,售出9號(hào)車票
          d號(hào)窗口 ,售出8號(hào)車票
          d號(hào)窗口 ,售出7號(hào)車票
          d號(hào)窗口 ,售出6號(hào)車票
          d號(hào)窗口 ,售出5號(hào)車票
          d號(hào)窗口 ,售出4號(hào)車票  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2012-10-29 15:56 eagledame

          兄弟 我執(zhí)行了你的程序1 和程序2 結(jié)果如上 。和你說的完全不一樣 。求解釋   回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2012-12-11 17:30 游客

          賣完票 記得ticket--  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2013-03-26 16:08 馬容亮

          我的理解 是java默認(rèn)多線程,但是 多線程并發(fā)會(huì)導(dǎo)致 公用變量處理不準(zhǔn)確,synchronized 同步 使 每個(gè)線程處理公用變量時(shí),能夠在一個(gè)完善的隊(duì)列中完成。  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2014-06-05 01:04

          @eagledame
          把值設(shè)置大點(diǎn)兒  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2014-08-16 11:32 文宇

          程序一和程序二都可以多窗口售票,只是票的總數(shù)不夠大,沒有等到b,c線程執(zhí)行,當(dāng)ticket=1000時(shí),就可以看到  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized)[未登錄] 2014-10-08 09:16 秋風(fēng)

          寫得太好了,謝謝作者對人類軟件事業(yè)進(jìn)步所做出的重大貢獻(xiàn)  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2014-10-13 11:14 zyongsh

          java線程調(diào)度是隨機(jī)的,即使統(tǒng)一程序在同一臺(tái)java虛擬機(jī)上執(zhí)行的結(jié)果也可能會(huì)不同@eagledame
            回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2014-11-28 10:18 家樂

          public void run()
          {
          while (true)
          {
          synchronized (this)
          {
          if (ticket > 0)
          {
          System.out.println(Thread.currentThread().getName() + "號(hào)窗口賣出" + this.ticket-- + "號(hào)票");
          }
          else
          {
          System.out.println(Thread.currentThread().getName() + "票已售完");
          break;
          }
          }
          try
          {
          Thread.sleep(1000);
          }
          catch (InterruptedException e)
          {
          e.printStackTrace();
          }
          }
          }  回復(fù)  更多評論   

          # re: java 同步鎖(synchronized)[未登錄] 2015-07-29 15:04 111

          你這個(gè)不算是多線程的同步吧
            回復(fù)  更多評論   

          # re: java 同步鎖(synchronized) 2015-07-29 18:40 龍井

          2、使用同步塊修改上面的例子修改錯(cuò)誤的,正確如下:
          public void run()
          {
          for(int i=1;i<50;i++)
          {
          //休眠1s秒中,為了使效果更明顯,否則可能出不了效果
          try {
          Thread.sleep(1000);
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          synchronized(this){
          if(ticket>0)
          {
          System.out.println(Thread.currentThread().getName()+"號(hào)窗口賣出"+this.ticket--+"號(hào)票");
          }
          }
          }
          }  回復(fù)  更多評論   


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


          網(wǎng)站導(dǎo)航:
           

          導(dǎo)航

          統(tǒng)計(jì)

          公告

          快樂每一天!

          Everything is an object!

          常用鏈接

          留言簿(2)

          隨筆檔案

          學(xué)習(xí)網(wǎng)站

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 鹰潭市| 泾源县| 泗阳县| 苍梧县| 四子王旗| 鹤庆县| 高陵县| 镇雄县| 萨迦县| 长沙市| 无为县| 呼和浩特市| 类乌齐县| 台山市| 祁门县| 龙泉市| 宣武区| 乐安县| 德兴市| 合江县| 嘉义市| 湘乡市| 松潘县| 健康| 衡阳县| 田东县| 姚安县| 息烽县| 平安县| 阿城市| 上思县| 拜城县| 仁布县| 澜沧| 宣武区| 乌鲁木齐县| 绍兴市| 托克托县| 将乐县| 平定县| 巴林右旗|