<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方法鎖定的對象是類對象。

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

    主站蜘蛛池模板: 国产高清不卡免费视频| 国产特黄特色的大片观看免费视频| **aaaaa毛片免费| 四虎成人精品国产永久免费无码| 久久精品国产亚洲AV无码偷窥| 精品国产免费观看一区| 亚洲AV一宅男色影视| 国产成人免费永久播放视频平台| 全黄大全大色全免费大片| 亚洲AV成人无码久久WWW| 亚洲男人的天堂在线| 麻豆高清免费国产一区| a级毛片免费高清毛片视频| 亚洲国产成人AV网站| 亚洲精品无码永久在线观看男男| 亚洲AV综合色区无码一区爱AV | 亚洲人成网77777色在线播放| 国产亚洲高清在线精品不卡| 亚洲天堂福利视频| 亚洲综合精品第一页| 亚洲六月丁香婷婷综合| 77777午夜亚洲| 亚洲爆乳AAA无码专区| 亚洲精品av无码喷奶水糖心| 亚洲午夜精品一区二区麻豆| 亚洲日韩国产一区二区三区在线 | 亚洲一级毛片免费看| 亚洲av日韩av无码av| 亚洲GV天堂无码男同在线观看| 亚洲中文字幕久久精品无码A| 亚洲精品无码中文久久字幕| 亚洲av成人一区二区三区在线播放 | 美女被吸屁股免费网站| 一级毛片大全免费播放| a级毛片黄免费a级毛片| 91麻豆最新在线人成免费观看| 成年性午夜免费视频网站不卡| 亚洲天堂免费在线视频| 91精品手机国产免费| 国产精品色午夜免费视频| 亚洲色欲色欲www在线丝|