java複習第6天---多線程----6.3--多線程同步
目錄
內容
1、線程同步
1.1、多線程安全問題的產生
- 示例1.1-1:電影院售票問題
現在電影院正在出售流浪地球電影票,100張。
- 只有1個售票窗口(單線程):此時不會產生安全問題
- 有多個售票窗口(多線程):當每個窗口售票都不一樣時,比如100張票分開,每個窗口分別銷售一疊票。此時,也不會出現安全問題
- 多個售票窗口(多線程):每個售票窗口都銷售者100張票,不分開。這時候,可能產生安全問題:
- 圖示1.1-1:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-A6mdhWOB-1590502296256)(./images/sealTicket.bmp)]
1.2、多線程安全問題產生代碼實現
package thread.create;
public class Ticket implements Runnable{
private int ticket = 10;
@Override
public void run() {
while(true) {
if(this.ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票");
ticket--;
}
}
}
}
package thread.create;
public class TestTicket1 {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t, "一號窗口");
Thread t2 = new Thread(t, "二號窗口");
Thread t3 = new Thread(t, "三號窗口");
t1.start();
t2.start();
t3.start();
}
}
測試結果:
一號窗口正在售賣10號票
三號窗口正在售賣9號票
二號窗口正在售賣9號票
二號窗口正在售賣7號票
一號窗口正在售賣7號票
三號窗口正在售賣7號票
一號窗口正在售賣4號票
三號窗口正在售賣4號票
二號窗口正在售賣4號票
三號窗口正在售賣1號票
二號窗口正在售賣0號票
一號窗口正在售賣-1號票
1.3、多線程安全問題分析
 某一時刻在打印語句輸出之前,3個線程ticket值可能相同,所有會打印相同的票,當某一個線程線程執行tickent = 0時,另外要給線程剛好執行tickent–那麼可能打印-號票。歸根結底,還是多線程共享同一資源,並且對共享資源進行操作引起的安全問題,怎麼解決呢?
爲解決多線程安全問題,java引入同步機制。同步機制有三種方式:
- 同步代碼塊
- 同步方法
- 鎖機制
下面分別介紹使用方法。
1.4、同步代碼
synchronized 關鍵字可以作用於方法中的某一塊區域,表示只對這個區域內的資源進行互斥訪問。
-
格式
synchronized(同步鎖) { 需要同步的代碼 }
-
同步鎖:對象的同步鎖只是一個概念,可以想象在對象上標記了一個鎖
- 所對象可以是任意類型
- 多個線程共享同一資源,需要使用相同的鎖
- 在任何時刻,最多有一個線程獲取同步鎖,進入代碼塊執行,其他線程對象只能在外等着(BOLOCKED)
-
同步代碼塊解決多線程安全問題:
package thread.create; public class SynTicket implements Runnable{ private int ticket = 10; @Override public void run() { while(true) { synchronized(this) { if(this.ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票"); ticket--; } } } } } package thread.create; public class TestSynTicket1 { public static void main(String[] args) { SynTicket t = new SynTicket(); Thread t1 = new Thread(t, "一號窗口"); Thread t2 = new Thread(t, "二號窗口"); Thread t3 = new Thread(t, "三號窗口"); t1.start(); t2.start(); t3.start(); } } 測試結果: 一號窗口正在售賣10號票 一號窗口正在售賣9號票 一號窗口正在售賣8號票 二號窗口正在售賣7號票 三號窗口正在售賣6號票 二號窗口正在售賣5號票 一號窗口正在售賣4號票 二號窗口正在售賣3號票 三號窗口正在售賣2號票 三號窗口正在售賣1號票
1.5、同步方法
-
同步方法:用synchronized 修飾的方法爲同步方法,能保證一個線程在訪問該方法的時候,其他線程在方法外等待
-
格式
修飾符 synchronized 返回值類型 方法名(參數列表) {需要同步的代碼塊}
-
所對象:
- 非static方法,所對象爲this
- static方法,鎖對象爲方法所在類的字節碼(.class)
-
用同步方法解決賣票完全問題:
package thread.create; public class SynMethodTicket implements Runnable{ private int ticket = 10; @Override public synchronized void run() { while(true) { if(this.ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票"); ticket--; } } } } package thread.create; public class TestSynMethodTicket { public static void main(String[] args) { SynMethodTicket t = new SynMethodTicket(); Thread t1 = new Thread(t, "一號窗口"); Thread t2 = new Thread(t, "二號窗口"); Thread t3 = new Thread(t, "三號窗口"); t1.start(); t2.start(); t3.start(); } } 測試結果: 一號窗口正在售賣10號票 一號窗口正在售賣9號票 一號窗口正在售賣8號票 一號窗口正在售賣7號票 一號窗口正在售賣6號票 一號窗口正在售賣5號票 一號窗口正在售賣4號票 一號窗口正在售賣3號票 一號窗口正在售賣2號票 一號窗口正在售賣1號票
-
總結:
- 鎖作用代碼塊越少,效率相對越高
- 能用同步代碼塊儘量不要用同步方法
1.6、lock鎖
java.util.concurrent.locks.Lock 鎖提供了比同步代碼塊和同步方法更廣泛的鎖定操作,具有同步代碼塊和同步方法全部功能,更加體現面向對象。
Lock鎖也稱同步鎖,加鎖和釋放鎖方法化 了,如下
-
public void lock:加鎖
-
public void unlock:釋放鎖
-
使用步驟:
- 成員位置創建ReentrantLock對象
- 在可能出現安全問題的代碼前 使用lock.lock()方法加鎖
- 在可能出現安全問題的代碼後使用lock.unlock()方法釋放鎖
-
lock鎖解決買票安全問題:
package thread.create; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TicketLock implements Runnable{ private int ticket = 10; Lock lock = new ReentrantLock(); @Override public void run() { while(true) { lock.lock(); if(this.ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票"); ticket--; } lock.unlock(); } } } package thread.create; public class TestTicketLock { public static void main(String[] args) { TicketLock t = new TicketLock(); Thread t1 = new Thread(t, "一號窗口"); Thread t2 = new Thread(t, "二號窗口"); Thread t3 = new Thread(t, "三號窗口"); t1.start(); t2.start(); t3.start(); } } 測試結果: 一號窗口正在售賣10號票 一號窗口正在售賣9號票 一號窗口正在售賣8號票 一號窗口正在售賣7號票 一號窗口正在售賣6號票 三號窗口正在售賣5號票 三號窗口正在售賣4號票 三號窗口正在售賣3號票 二號窗口正在售賣2號票 二號窗口正在售賣1號票
1.7、靜態同步方法
靜態方法的鎖對象是所在類的class屬性,即所在類的字節碼類。
後記 :
本項目爲參考某馬視頻開發,相關視頻及配套資料可自行度娘或者聯繫本人。上面爲自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785
前端項目源代碼地址:https://gitee.com/gaogzhen/vue-leyou
後端JAVA源代碼地址:https://gitee.com/gaogzhen/JAVA