老生常談的一個問題,就bank的那個例子,同一張卡同時取錢,併發2個,每一個取800,但卡里只有1000,不能讓2我的同時操做.這裏就須要線程同步.好比下面的DEMO. java
首先定義一個帳戶類: 編程
package org.credo.thread.tongbu; public class Account { private String account; private double money; public Account(String account,double money){ this.account=account; this.money=money; } //根據account重寫hashcode和equals方法. public int hasCode(){ return account.hashCode(); } public boolean equals(Object obj){ if(this==obj){ return true; } if(obj!=null && obj.getClass()==Account.class){ Account target=(Account)obj; return target.getAccount().equals(account); } return false; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } }再就是業務類:
package org.credo.thread.tongbu; public class Draw extends Thread{ private Account account; private double hopeGetMoney; public Draw(String name,Account account,double hopeGetMoney){ super(name); this.account=account; this.hopeGetMoney=hopeGetMoney; } //當多個線程修改同一個共享數據的時候,將會引起數據問題. public void run(){ if(account.getMoney() >= hopeGetMoney){ System.out.println(account.getAccount()+"---"+getName()+"取錢成功,取出金額爲:"+hopeGetMoney); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } //修改金額 account.setMoney(account.getMoney()-hopeGetMoney); System.out.println("餘額爲:"+account.getMoney()); }else{ System.out.println("餘額不足,取款失敗!"); } } }測試類:
package org.credo.thread.tongbu; public class NoSynchronizedTest { public static void main(String[] args) { //create a new account Account account=new Account("印度人", 1000); new Draw("印度人A", account, 800).start(); new Draw("印度人B", account, 800).start(); } }結果是:
印度人---印度人B取錢成功,取出金額爲:800.0
印度人---印度人A取錢成功,取出金額爲:800.0
餘額爲:200.0
餘額爲:-600.0 安全
天然是錯的,畢竟這不是信用卡. 多線程
在這種場景下,account這個帳戶必然要是必定時間內只能一我的操做的,是須要線程的同步的. 併發
以下code:在業務類的run方法總加入synchronized代碼塊. 工具
public void run(){ //使用account做爲同步監視器,任何線程進入下面同步代碼塊以前必須先得到對account帳戶的鎖定. //其餘線程沒法得到鎖,也就沒法修改 //這種作法符合, 加鎖---修改---釋放鎖 的邏輯. synchronized (account) { if(account.getMoney() >= hopeGetMoney){ System.out.println(account.getAccount()+"---"+getName()+"取錢成功,取出金額爲:"+hopeGetMoney); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } //修改金額 account.setMoney(account.getMoney()-hopeGetMoney); System.out.println("餘額爲:"+account.getMoney()); }else{ System.out.println("餘額不足,取款失敗!"); } } //結束,釋放同步鎖. }印度人---印度人A取錢成功,取出金額爲:800.0
synchronized(obj){
//此處代碼就是同步代碼塊
} 性能
與同步代碼塊對應的,java的多線程的安全支持還提供了同步方法,同步方法就是使用synchronized關鍵字去修飾某個方法.對於同步方法而言,其同步監視器就是this,就是該對象自己. 測試
用過使用同步方法能夠很方便的實現線程安全的類,線程安全的類具備以下特徵: ui
synchronized關鍵字能夠修飾方法,代碼塊,但不能修改構造器,屬性等. this
在Account類中加入下面代碼:
public synchronized void process(double hopeGetMoney){ if(money >= hopeGetMoney){ System.out.println(Thread.currentThread().getName()+"取錢成功,取出金額爲:"+hopeGetMoney); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } //修改金額 this.money=money-hopeGetMoney; System.out.println("餘額爲:"+money); }else{ System.out.println("餘額不足,取款失敗!"); } }
修改業務類中的run()方法爲:
public void run(){ account.process(hopeGetMoney); }印度人A取錢成功,取出金額爲:800.0
好比StringBuffer是多線程安全的,但性能很差.StringBuilder有好的性能,但線程是不安全的.
釋放:
不釋放:
先貼代碼,上一章最後的代碼,不變,只修改account代碼以下:
public class Account { private String account; private double money; private final ReentrantLock lock=new ReentrantLock(); public Account(String account,double money){ this.account=account; this.money=money; } public void process(double hopeGetMoney){ try { lock.lock(); if(money >= hopeGetMoney){ System.out.println(Thread.currentThread().getName()+"取錢成功,取出金額爲:"+hopeGetMoney); try { Thread.sleep(1); } catch (Exception e) { e.printStackTrace(); } //修改金額 this.money=money-hopeGetMoney; System.out.println("餘額爲:"+money); }else{ System.out.println("餘額不足,取款失敗!"); } } finally { lock.unlock(); } } //根據account重寫hashcode和equals方法.使用的是lock.輸出爲:
印度人A取錢成功,取出金額爲:800.0
餘額爲:200.0
餘額不足,取款失敗!
Lock是控制多個線程對共享資源進行訪問的工具.一般,鎖提供了對共享資源的獨佔訪問,每次只能有一個線程對lock對象加鎖,線程開始訪問共享資源以前應得到Lock對象.
同步鎖裏面須要理解的東西也不少,這裏........尼瑪快過年了,實在沒心情搞了.
死鎖就是兩個線程互相等待對方釋放同步監視器時就回發生死鎖.Java虛擬機沒有檢測,也沒有采起措施來處理死鎖狀況,因此多線程編程時要注意這個狀況.發生死鎖的時候,整個程序沒錯誤沒異常,沒提示,只是說有線程都處於阻塞狀態,沒法繼續.