java解決共用資源競爭

來源:互聯網
上載者:User

標籤:訪問   account   test   完成   span   system   hdr   smo   競爭   

由於多線程的實現,在運行一個程式的時候可能會有很多的線程在同時運行,但是線程的調度並不是可見的,所以不會知道一個線程什麼時候在運行,比如說 你坐在桌子前手拿著叉子,正要去叉盤中的最後一片食物,當你的叉子就要夠得著他的時候,這片食物突然消失了,因為你的線程掛起了,另一個用餐者進入並吃掉了它,所以這就是在使用多線程時會出現的問題,對於並發的任務,你需要一種方式來防止兩個線程同時訪問一段資源,基本上所有的併發模式在解決線程衝突上都是採用序列化訪問共用資源的方案,這就意味著在cpu給定的時刻,只允許一個線程訪問共用資源,通常是在代碼前加上一條鎖定語句來實現的,一旦執行到鎖定語句上的時候,就會產生一種互斥的效果,所以這種機制常常成為互斥量,

第一種 關鍵字synchronizedjava為放置資源衝突提供了內建的支援 關鍵字synchronized,當代碼執行到被synchronized保護的程式碼片段的時候,它會檢查鎖是否可用,然後擷取鎖,釋放鎖。

注意 :* 共用資源一般是以對象形存在的記憶體片段,但也可以是檔案、輸入\輸出連接埠,或者印表機,要控制對共用資源的訪問,得先把它包進一個對象,然後把所有要訪問這個資源的方法標記為synchronized,如果某個線程處於一個對標記synchronized的方法的調用中,那麼在這個方法返回前。其他所有要調用這個類中任何一個標記為synchronized的方法的線程都會被阻塞。 

 

synchronized的缺陷:當某個線程進入同步方法獲得對象鎖,那麼其他線程訪問這裡對象的同步方法時,必須等待或者阻塞,這對高並發的系統是致命的,這很容易導致系統的崩潰。如果某個線程在同步方法裡面發生了死迴圈,那麼它就永遠不會釋放這個對象鎖,那麼其他線程就要永遠的等待。這是一個致命的問題。

 

當然同步方法和同步代碼塊都會有這樣的缺陷,只要用了synchronized關鍵字就會有這樣的風險和缺陷。既然避免不了這種缺陷,那麼就應該將風險降到最低。這也是同步代碼塊在某種情況下要優於同步方法的方面。例如在某個類的方法裡面:這個類裡面聲明了一個對象執行個體,SynObject so=new SynObject();在某個方法裡面調用了這個執行個體的方法so.testsy();但是調用這個方法需要進行同步,不能同時有多個線程同時執行調用這個方法。

 

這時如果直接用synchronized修飾調用了so.testsy();代碼的方法,那麼當某個線程進入了這個方法之後,這個對象其他同步方法都不能給其他線程訪問了。假如這個方法需要執行的時間很長,那麼其他線程會一直阻塞,影響到系統的效能。

 

如果這時用synchronized來修飾代碼塊:synchronized(so){so.testsy();},那麼這個方法加鎖的對象是so這個對象,跟執行這行代碼的對象沒有關係,當一個線程執行這個方法時,這對其他同步方法時沒有影響的,因為他們持有的鎖都完全不一樣。

 

所有的對象自動含有單一的鎖也叫做監視器,當在對象上調用任意synchronized方法的時候,此對象都被加鎖,這時該對象上的其他synchronized方法只有等到前一個方法調用完畢並且釋放了所之後才能被調用,還有一個需要注意的 將共用資源設定為private是很有必要的,為了防止其他線程直接存取資源,那麼加了鎖也沒有用,

一個類的對象鎖和另一個類的對象鎖是沒有關聯的,當一個線程獲得A類的對象鎖時,它同時也可以獲得B類的對象鎖。



然後一個線程可以多次獲得對象的鎖,比如一個訪問的這個被synchronized標記的方法當中在同一個對象上調用了第二個被synchronized標記的方法,後者又調用了另一個同一個對象上被synchronized標記的方法,就會發生這種情況,虛擬機器負責跟蹤對象被加鎖的次數,如果一個對象被完全解鎖,其計數器變為0,線上程第一次給對象加鎖的時候,計數器變為1,然後每當這個相同的線程在這個對象上獲得鎖的時候。依次累加1

 

java的對象鎖和類鎖:java的對象鎖和類鎖在鎖的概念上基本上和內建鎖是一致的,但是,兩個鎖實際是有很大的區別的,對象鎖是用於對象執行個體方法,或者一個對象執行個體上的,類鎖是用於類的靜態方法或者一個類的class對象上的。我們知道,類的對象執行個體可以有很多個,但是每個類只有一個class對象,所以不同對象執行個體的對象鎖是互不干擾的,但是每個類只有一個類鎖。但是有一點必須注意的是,其實類鎖只是一個概念上的東西,並不是真實存在的,它只是用來協助我們理解鎖定執行個體方法和靜態方法的區別的

 

先定義一個共用資源的對象 示範一下對象鎖

package test.thread.sx;public class BankAccount {    //餘額      private int banlance = 500;      //查詢      public int getBalance(){          return banlance;      }      //取款      public void withdraw(int amount){          banlance = banlance - amount;      }      //存款      public void deposit(int amount){          banlance = banlance + amount;      }  }

然後定義任務方法

package test.thread.sx;public class TesMony implements Runnable {      //所有Thread多線程線程都共用Runnable(介面對象)和account對象      private BankAccount account = new BankAccount();      @Override      public void run() {          for(int i = 0; i< 5; i++){           //總共取款5次              makeWithdraw(100);          //每次取款100              if(account.getBalance() < 0){                  System.out.println("☆"+Thread.currentThread().getName()+"   透支了!");              }          }      }        /**      * makeWithdraw 賬戶取款      * @param amount 取款金額<br />      * 列印log記錄取款過程      * */      private  synchronized void makeWithdraw(int amount){          if(account.getBalance() >= amount){          //如果餘額足夠則取款              System.out.println("☆"+Thread.currentThread().getName()+"   準備取款!");              try {                  Thread.sleep(500);              } catch (InterruptedException e) {                  System.out.println(Thread.currentThread().getName()+"   準備取款,等待0.5s線程中斷!"+e.getMessage());              }              account.withdraw(amount);              System.out.println("☆"+Thread.currentThread().getName()+"   完成"+amount+"取款!餘額為"+account.getBalance());      }else{          //餘額不足則提示              System.out.println("☆"+"餘額不足以支付"+Thread.currentThread().getName()+amount+"   的取款,餘額為"+account.getBalance());          }      }  }

然後測試一下

package test.thread.sx;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Tess {       public static void main(String[] args) {          //定義任務的時候 我自己不理解線程的時候犯了一個錯誤,就是每一個新線程都會給它一個新的任務 也就是新的TesMony對象  那其實就不存在資源競爭了
//因為那根本就是兩個對象 資源競爭是針對一個對象來說的 而且互斥鎖也是鎖在同一個對象上才會出現互斥, TesMony target = new TesMony(); //建立李琦和他老婆兩個線程實現取款(同時) Thread lq = new Thread(target); lq.setName("羅密歐"); Thread lqwf = new Thread(target); lqwf.setName("朱麗葉"); //調用Thread對象的start()方法,啟動線程,執行run()方法(OS) lq.start(); lqwf.start(); } }

第二種 java又提供了另一種方式顯示LOCK的方式 這也是以惡搞被互斥調用的鎖,並使用lock和unlock方法標識臨界資源,它和synchronized關鍵字的區別,當我們使用synchronized關鍵字標記的程式碼片段出現某些錯誤的時候 會拋出一個異常 但是不會有機會去做任何的補救工作,但是使用lock對象的方法去加鎖 解鎖,的時候我們可以使用finnaly字句去做一些處理。那麼上面的代碼可以這樣他修改

package test.thread.sx;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class TesMony implements Runnable {    private Lock lock = new ReentrantLock();    // 所有Thread多線程線程都共用Runnable(介面對象)和account對象    private BankAccount account = new BankAccount();    @Override    public void run() {        for (int i = 0; i < 5; i++) { // 總共取款5次            makeWithdraw(100); // 每次取款100            if (account.getBalance() < 0) {                System.out.println("☆" + Thread.currentThread().getName() + "   透支了!");            }        }    }    /**     * makeWithdraw 賬戶取款     *      * @param amount     *            取款金額<br />     *            列印log記錄取款過程     */    private void makeWithdraw(int amount) {        lock.lock();        try {            if (account.getBalance() >= amount) { // 如果餘額足夠則取款                System.out.println("☆" + Thread.currentThread().getName() + "   準備取款!");                Thread.sleep(500);                account.withdraw(amount);                System.out.println(                        "☆" + Thread.currentThread().getName() + "   完成" + amount + "取款!餘額為" + account.getBalance());            } else { // 餘額不足則提示                System.out.println("☆" + "餘額不足以支付" + Thread.currentThread().getName() + amount + "   的取款,餘額為"                        + account.getBalance());            }        } catch (InterruptedException e) {            System.err.println("中斷了請等待");        } finally {            lock.unlock();        }    }}

java解決共用資源競爭

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.