Java學習筆記45(多線程二:安全問題以及解決原理),java學習筆記
安全執行緒問題以及解決原理:
多個線程用一個共用資料時候出現安全問題
一個經典案例:
電影院賣票,共有100座位,最多賣100張票,買票方式有多種,網上購買、自主售票機、排隊購買
三種方式操作同一個共用資料,這時候會出現安全問題:
樣本:
package demo1;public class Tickets implements Runnable { private int ticket = 100; public void run(){ while(true){ if (ticket>0) { System.out.println(Thread.currentThread().getName()+"出售第"+ticket--+"張票"); } } }}
package demo1;public class ThreadDemo { public static void main(String[] args) { Tickets t = new Tickets(); Thread t0 = new Thread(t); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t0.start(); t1.start(); t2.start(); }}
一般不會出現問題,但是要想到這種問題
但是,假設只剩下最後最後一張票,一個線程搶到CPU資源執行,在判斷結束時候,CPU資源被其他線程搶到,其他線程判斷然後執行,
這時候輪到開始時候的線程,由於已經判斷完,繼續執行,這時候票數就會變成負數,這裡就出現了問題
解決方案:
同步代碼塊
原理:一個線程進入資料操作的時候,阻止其他線程執行
package demo1;public class Tickets implements Runnable { private int ticket = 100; private Object obj1 = new Object(); public void run() { while (true) { synchronized (obj1) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "張票"); } } } }}
不過,雖然安全了,但是運行速度下降
但是,我們為了安全性可以不顧及速度,無論如何都要保證安全性
這裡傳入的對象參數簡稱作:同步鎖,專業名稱:對象監視器
原理:
沒有鎖的線程不能執行,只能等待
線程遇到同步代碼塊後判斷是否有同步鎖,如果有,拿走鎖,進入同步中執行,執行完畢後將鎖對象還回去
另一個線程遇到代碼塊後沒有鎖,無法進入,原來的線程把鎖還回去之後新線程再擷取鎖,迴圈下去
這裡明顯可以看出,這麼多的過程,速度自然就慢下來了
採用同步方法解決問題:
優點:代碼量更低
package demo1;public class Tickets implements Runnable { private int ticket = 100; public void run() { while (true) { payTicket(); } } public synchronized void payTicket() { //同步方法的對象鎖是本類對象引用:即為this //靜態方法的鎖是本類類名.class if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "張票"); } }}
缺點:如果出現了異常,方法的鎖對象沒有釋放,不出同步,鎖不會釋放
這裡就需要用到一個Lock介面:
提供了更廣泛的鎖定操作
改進之前的售票案例:
package demo1;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Tickets implements Runnable { private int ticket = 100; private Lock lock = new ReentrantLock(); public void run() { while (true) { lock.lock(); if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "張票"); } lock.unlock(); } }}
死結:
同步鎖引發的弊端:
當線程任務中出現了多個同步時,如果同步中嵌套了其他的同步,這時候就會引發一種現象,程式出現無限等待,這種現象稱之為死結
通俗解釋:兩個人吃一碗面,卻只有一雙筷子,兩個人一人搶到一支筷子,規定不能用手抓,這時候就無法吃面
代碼實現:
package demo1;public class LockA { private LockA(){} public final static LockA locka =new LockA();}
package demo1;public class LockB { private LockB(){} public final static LockB lockb =new LockB();}
package demo1;public class DeadLock implements Runnable { private int i = 0; public void run() { while (true) { if (i % 2 == 0) { synchronized (LockA.locka) { System.out.println("if...locka"); synchronized (LockB.lockb) { System.out.println("if...lockb"); } } } else { synchronized (LockB.lockb) { System.out.println("else...lockb"); synchronized (LockA.locka) { System.out.println("else...locka"); } } } i++; } }}
package demo1;public class DeadLockDemo { public static void main(String[] args) { DeadLock dead = new DeadLock(); Thread t0 = new Thread(dead); Thread t1 = new Thread(dead); t0.start(); t1.start(); }}
運行後發現,會卡在某一處不動,但是並沒有停止