<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    和風細雨

    世上本無難事,心以為難,斯乃真難。茍不存一難之見于心,則運用之術自出。

    線程的互斥

    多線程操作同一實例的問題

    在多線程環境中,經常有兩個以上線程操作同一實例的問題,無論是并行Parallel環境還是并發Concurrent環境,都有發生有多個線程修改同一變量的問題,如果這個變量是成員變量,多線程將會給程序帶來破壞性的影響。請見以下代碼。

    資源庫類

    public class ResourceLib {
      private long count1;

      private long count2;

      public ResourceLib(int count) {
        this.count1 = count;     
        this.count2 = count;
      }

      /**
       * 取回資源
       * 加上synchronized才是線程安全
       *
       * @param count
       */
      public void fetch(int count) {
        count1 += count;   
        mockLongTimeProcess();   
        count2 += count;   
        checkTwoCount(count);
      }

      /**
       * 送出資源
       * 加上synchronized才是線程安全
       *
       * @param count
       * @return
       */
      public void send(int count) {
        count1 -= count;   
        mockLongTimeProcess();   
        count2 -= count;
        checkTwoCount(count);
      }

       /**
       * 模擬一個耗時過程
       *
       */
      private void mockLongTimeProcess(){
        try{
          Thread.sleep(1000);
        }
        catch(Exception ex){
          ex.printStackTrace();
        }
      }

      private void checkTwoCount(int borrowCount) {
        if (count1 != count2) {
          System.out.println(count1 + "!= " + count2);
          System.exit(0);
        } else {
          System.out.println(count1 + "==" + count2);
        }
       
        if (Math.abs(count1) > 10000000 || Math.abs(count2) > 10000000) {
          count1 = 0;
          count2 = 0;
        }
      }

      public static void main(String[] args) {
        ResourceLib lib = new ResourceLib(10000);

        for (int i = 1; i < 20; i++) {
          new Supplier(String.valueOf(i), i, lib);
        }

        for (int i = 1; i < 10; i++) {
          new Comsumer(String.valueOf(i), i, lib);
        }
      }
    }

    取資源和給資源的兩個線程

    public class Comsumer implements Runnable{
      private ResourceLib resourceLib;
      private int count;
     
      public Comsumer(String name,int count,ResourceLib resourceLib){
        this.count=count;
        this.resourceLib=resourceLib;
       
        Thread thread=new Thread(this);
        thread.start();
      }
     
      public void run(){
        while(true){
          resourceLib.send(count);
        }
      }
    }

    public class Supplier implements Runnable{
      private ResourceLib resourceLib;
      private int count;
     
      public Supplier(String name,int count,ResourceLib resourceLib){
        this.count=count;
        this.resourceLib=resourceLib;
       
        Thread thread=new Thread(this);
        thread.start();
      }
     
      public void run(){
        while(true){
          resourceLib.fetch(count);
        }
      }
    }

    運行結果

    在main函數中,程序啟動了多個消費者線程和生產者線程,消費者線程在不斷減少count1和count2;生產者線程在不斷增加count1和count2,在單線程環境中,程序絕不會出現count1和count2不相等的情況,而多線程環境中,可能有一個線程在檢查count1和count2時,其中一個已經被另一個線程所修改。
    因此導致了兩個值不相等的情況發生。

    運行結果之一
    10145!= 10001
    10145!= 10003
    10145!= 10006
    10145!= 10010
    10145!= 10015
    10145!= 10021
    10145!= 10028
    10145!= 10036
    10145!= 10045
    10145!= 10055
    10145!= 10066

    另一個經典多線程實例:銀行取款

    package com.sitinspring.unsafebank;

    public class Bank{
      private int count;
     
      public Bank(int count){
        this.count=count;
      }
     
      public void withdraw(int money){
        if(count>money){
          mockLongTimeProcess();// 模擬耗時過程
          count-=money;
          System.out.println("提走"+money+" 現有"+count);    
        }
        else{
          System.out.println(" 現有數量"+count+"小于"+money+" 不能提取");
        }
       
        checkCount();
      }
     
      public void checkCount(){
        if(count<0){
          System.out.println(count + "< 0 ");
          System.exit(0);
        }
      }

     /**
       * 模擬一個耗時過程
       *
       */
      private void mockLongTimeProcess(){
        try{
          Thread.sleep(1000);
        }
        catch(Exception ex){
          ex.printStackTrace();
        }
      }
     
      public static void main(String[] args){
        Bank bank=new Bank(1000);
       
        for(int i=1;i<10;i++){
          new Customer(i*i*i,bank);
        }
      }
    }

    客戶類及講述

    public class Customer implements Runnable{
      private Bank bank;
      private int count;
     
      public Customer(int count,Bank bank){
        this.count=count;
        this.bank=bank;
       
        Thread thread=new Thread(this);
        thread.start();
      }
     
      public void run(){
        while(true){
          bank.withdraw(count);
        }
      }
    }

    在單線程環境中,提款時銀行的總數絕不會是負數,但在多線程環境中,有可能在一個線程A符合條件在進行耗時運算和網絡數據傳遞時,另一個線程B已經把錢提走,總數已經發生變化,結果A線程再提款時總錢數已經減小了,因此致使銀行總錢數小于零。

    解決方法:在對成員變量進行修改的函數前加上synchronized關鍵字

    synchronized方法又被成為”同步“方法。當一個方法加上關鍵字synchronized聲明之后,就可以讓一個線程操作這個方法。“讓一個線程操作”并不是說只能讓某一個特定的線程操作而已,而是指一次只能讓一個線程執行,也就是說,在一個線程沒有退出同步方法前,其它線程絕無可能進入這個同步方法和其它并列的同步方法,只能在外面排隊等候。
    一個實例的synchronized方法只能允許1次一個線程執行。但是非synchronized方法就沒有這個限制,它可以供2個以上的線程執行。

    修改后的線程安全的Bank類

    public class Bank{
      private int count;
     
      public Bank(int count){
        this.count=count;
      }
     
      public synchronized void withdraw(int money){
        if(count>money){
          mockLongTimeProcess();// 模擬耗時過程
          count-=money;
          System.out.println("提走"+money+" 現有"+count);    
        }
        else{
          System.out.println(" 現有數量"+count+"小于"+money+" 不能提取");
        }
       
        checkCount();
      }
     
      public void checkCount(){
        if(count<0){
          System.out.println(count + "< 0 ");
          System.exit(0);
        }
      }
    。。。、// 部分代碼省略
    }



    修改后的線程安全的ResourceLib類

    public class ResourceLib {
      private long count1;
      private long count2;

      public synchronized void fetch(int count) {
        count1 += count;   
        mockLongTimeProcess();   
        count2 += count;   
        checkTwoCount(count);
      }

      public synchronized void send(int count) {
        count1 -= count;   
        mockLongTimeProcess();   
        count2 -= count;
        checkTwoCount(count);
      }

      public void checkTwoCount(int borrowCount) {
        if (count1 != count2) {
          System.out.println(count1 + "!= " + count2);
          System.exit(0);
        } else {
          System.out.println(count1 + "==" + count2);
        }
       
        if (Math.abs(count1) > 10000000 || Math.abs(count2) > 10000000) {
          count1 = 0;
          count2 = 0;
        }
      }
    }

    注:部分代碼省略



    執行之后

    在一個執行synchronized方法的線程執行結束后,鎖定即被釋放, 其它不得其門而入的線程開始爭搶鎖定,一定會有一個線程獲取鎖定,沒有搶到的線程只好再繼續等候.
    注意: 非靜態的synchronized方法鎖定的對象是實例,靜態的synchronized方法鎖定的對象是類對象。

    同步塊

    以下同步方法可用右邊的同步塊代替:
    public synchronized void fun(){
        ………
    }

    與左邊同步方法對等的同步塊:
    public void fun(){
       synchronized(this){
         ………
       }
    }

    同步塊和同步方法的比較

    1)同步方法鎖定的類的實例或類對象,同步塊則可以換成任意實例,靈活性更高。
    2)有時需要多個鎖定而不是一個,如函數A和函數B需要鎖定1,函數B和函數C需要鎖定2,這時如果使用同步方法無疑會鎖定A和C,造成程序效率的降低。這時最應該使用同步塊。

    什么時候該加同步synchronized

    如果一個函數或代碼塊有可能被多個線程進入,而這個函數或代碼塊又修改了類的成員變量,則這個這個函數或代碼塊就應該加上同步synchronized。
    如果一個函數或代碼有可能被多個線程進入,而這個函數或代碼塊只是讀取類的成員變量,則這個這個函數或代碼塊就不該加上同步synchronized。

     

    posted on 2008-02-22 12:43 和風細雨 閱讀(489) 評論(1)  編輯  收藏 所屬分類: 線程

    評論

    # re: 線程的互斥 2008-04-13 23:23 javafans_2008@163.com

    注意: 非靜態的synchronized方法鎖定的對象是實例,靜態的synchronized方法鎖定的對象是類對象。

    請問下:
    實例和類對象有什么區別呀??   回復  更多評論   

    主站蜘蛛池模板: 亚洲国产高清在线| 亚洲女人被黑人巨大进入| 久久精品国产亚洲AV大全| 人人揉揉香蕉大免费不卡| 亚洲乱亚洲乱妇无码麻豆| 国产男女爽爽爽免费视频| 中文字幕精品无码亚洲字 | 亚洲日韩中文字幕在线播放| 一个人晚上在线观看的免费视频| 国产午夜亚洲不卡| 国产午夜无码精品免费看| 亚洲精品第五页中文字幕| 120秒男女动态视频免费| 亚洲mv国产精品mv日本mv| 成年女人毛片免费播放视频m| 亚洲人成网站免费播放| 免费A级毛片无码A| 99在线免费观看| 亚洲精品综合久久中文字幕| 四虎影视www四虎免费| 一级做a爱过程免费视频高清| 亚洲精品国产成人片| h片在线免费观看| 精品亚洲成a人在线观看| 久久久精品国产亚洲成人满18免费网站| 色www永久免费| 亚洲mv国产精品mv日本mv| 亚洲AV伊人久久青青草原| 在线毛片片免费观看| 亚洲中文字幕日本无线码| 波多野结衣免费视频观看| 在线成人精品国产区免费| 亚洲娇小性色xxxx| 亚洲一区二区三区在线播放| 在线日本高清免费不卡| 边摸边吃奶边做爽免费视频99 | 中国一级特黄的片子免费| 亚洲欧洲日产专区| 亚洲午夜无码AV毛片久久| 4455永久在线观免费看| 一级毛片免费播放男男|