Lock是一個接口,ReentrantLock是它的實現類,下面經過「取款機案例」來剖析它的4個經常使用方法。java
public class Bank { private static double money = 10000; public void login(Thread currentUserThread) { System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額 : " + money); } public void logout() { System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額 : " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款 : " + withdrawMoney + " 當前銀行餘額 : " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動媽媽線程 Thread motherThread = new Thread("媽媽") { public void run() { try { //媽媽登陸 bank.login(Thread.currentThread()); //過5秒取 10000 Thread.sleep(5000); bank.withdraw(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }; motherThread.start(); } }
結果:this
媽媽 登陸進入銀行 當前銀行餘額 : 10000.0 爸爸 登陸進入銀行 當前銀行餘額 : 10000.0 爸爸 取款 : 10000.0 當前銀行餘額 : 0.0 媽媽 當前銀行餘額 : 0.0 餘額不夠
媽媽登陸顯示銀行餘額爲10000,可是當她取錢時卻顯示「餘額不足」,產生了數據不一致。spa
public class Bank { private static double money = 10000; private Lock lock = new ReentrantLock(); public void login(Thread currentUserThread) { lock.lock();//登陸加鎖 System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額 : " + money); } public void logout() { lock.unlock();//退出釋放鎖 System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額 : " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款 : " + withdrawMoney + " 當前銀行餘額 : " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); //爸爸退出 bank.logout(); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動媽媽線程 Thread motherThread = new Thread("媽媽") { public void run() { try { //媽媽登陸 bank.login(Thread.currentThread()); //過5秒取 10000 Thread.sleep(5000); bank.withdraw(10000); //媽媽退出 bank.logout(); } catch (InterruptedException e) { e.printStackTrace(); } } }; motherThread.start(); } }
結果:線程
爸爸 登陸進入銀行 當前銀行餘額 : 10000.0 爸爸 取款 : 10000.0 當前銀行餘額 : 0.0 爸爸 退出銀行 媽媽 登陸進入銀行 當前銀行餘額 : 0.0 媽媽 當前銀行餘額 : 0.0 餘額不夠 媽媽 退出銀行
lock() 和unlock()成對出現,在login(Thread currentUserThread) 登陸方法中調用 lock() ,在 logout()退出方法中調用了unlock()。
也就是說,Lock類的鎖機制容許在不一樣的方法中加鎖和解鎖,而synchronized關鍵字只能在同一個方法中加鎖和解鎖。code
public class Bank { private static double money = 10000; private Lock lock = new ReentrantLock(); public void login(Thread currentUserThread) { //判斷是否已經有線程登陸 if (!lock.tryLock()) { System.out.println(Thread.currentThread().getName() + " 有人已經登陸進入銀行 請稍等"); } else { System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額: " + money); } } public void logout() { lock.unlock();//退出釋放鎖 System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款: " + withdrawMoney + " 當前銀行餘額: " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); //爸爸退出 bank.logout(); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動媽媽線程 Thread motherThread = new Thread("媽媽") { public void run() { //媽媽登陸 bank.login(Thread.currentThread()); } }; motherThread.start(); } }
結果:blog
媽媽 有人已經登陸進入銀行 請稍等 爸爸 登陸進入銀行 當前銀行餘額: 10000.0 爸爸 取款: 10000.0 當前銀行餘額: 0.0 爸爸 退出銀行
public class Bank { private static double money = 10000; private Lock lock = new ReentrantLock(); public void login(Thread currentUserThread) { //若是登陸不成功,10秒後再從新嘗試得到鎖 try { if (!lock.tryLock(10, TimeUnit.SECONDS)) { System.out.println(Thread.currentThread().getName() + " 有人已經登陸進入銀行,請稍等"); } else { System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額: " + money); } } catch (InterruptedException e) { e.printStackTrace(); } } public void logout() { lock.unlock();//退出釋放鎖 System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款: " + withdrawMoney + " 當前銀行餘額: " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); //爸爸退出 bank.logout(); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動媽媽線程 Thread motherThread = new Thread("媽媽") { public void run() { //媽媽登陸 bank.login(Thread.currentThread()); } }; motherThread.start(); } }
結果媽媽10秒後從新嘗試獲得鎖登陸進入了銀行 :接口
爸爸 登陸進入銀行 當前銀行餘額: 10000.0 爸爸 取款: 10000.0 當前銀行餘額: 0.0 爸爸 退出銀行 媽媽 登陸進入銀行 當前銀行餘額: 0.0
public class Bank { private static double money = 10000; private Lock lock = new ReentrantLock(); public void login(Thread currentUserThread) { lock.lock(); System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額 : " + money); } public void logout() { lock.unlock();//退出釋放鎖 System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款: " + withdrawMoney + " 當前銀行餘額: " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動媽媽線程 Thread motherThread = new Thread("媽媽") { public void run() { try { Thread.sleep(1000); bank.login(Thread.currentThread()); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+" 登陸超時被中斷"); } } }; motherThread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } motherThread.interrupt(); } }
結果:get
媽媽線程一直等待爸爸線程釋放鎖,結果形成死鎖。媽媽線程調用motherThread.interrupt(); 中斷也不起作用。it
public class Bank { private static double money = 10000; private Lock lock = new ReentrantLock(); public void login(Thread currentUserThread) throws InterruptedException { lock.lockInterruptibly();//運行等待線程被中斷 System.out.println(Thread.currentThread().getName() + " 登陸進入銀行" + " 當前銀行餘額 : " + money); } public void logout() { lock.unlock();//退出釋放鎖 System.out.println(Thread.currentThread().getName() + " 退出銀行"); } public double withdraw(double withdrawMoney) { if (this.money < withdrawMoney) { System.out.println(Thread.currentThread().getName() + " 當前銀行餘額: " + this.money + " 餘額不夠"); return 0; } this.money -= withdrawMoney; System.out.println(Thread.currentThread().getName() + " 取款: " + withdrawMoney + " 當前銀行餘額: " + this.money); return withdrawMoney; } }
public class TestLock { public static void main(String[] args) { final Bank bank = new Bank(); //啓動爸爸線程 Thread fatherThread = new Thread("爸爸") { public void run() { try { //爸爸登陸 bank.login(Thread.currentThread()); //過2秒取10000 Thread.sleep(2000); bank.withdraw(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }; fatherThread.start(); // 啓動 媽媽 線程 Thread motherThread = new Thread("媽媽") { public void run() { try { Thread.sleep(1000); bank.login(Thread.currentThread()); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+" 登陸超時被中斷"); } } }; motherThread.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } motherThread.interrupt(); } }
結果:io
爸爸 登陸進入銀行 當前銀行餘額 : 10000.0
媽媽 登陸超時被中斷
爸爸 取款: 10000.0 當前銀行餘額: 0.0