併發編程之槍械爭奪
需求如下圖:狙擊手們和備彈手們同時爭搶一把槍,槍同時只能被一人持有,且備彈容量上限爲20。狙擊手則使用槍械開槍子彈 -1,備彈手則給槍械上子彈 +1.
問題分析:
基於面向對象的編程思想,我們首先需要對三者進行抽象,需要創建以下三種類型。
- 創建槍械的類,槍械有shot和put方法,有槍械名稱和子彈容量等屬性。
- 創建狙擊手類,由於我們是多線程編程,則實現runnable接口,在run方法中使用無限循環調用shot。
- 創建備彈手類,實現runnable接口,在run方法中使用無限循環調用put方法。
在《Effective Java》中,對wait()、notify()、notifyAll()有所描述,作者認爲wait()和notifyAll()是組合使用的,結合實際情況來看,確實是這樣。如果使用notify()只喚醒一個線程,但是該線程卻沒有達到獲取鎖的條件,調用了wait()此時則會發生死鎖的情況。
wait()使用範式:
while(不滿足條件){
wait();
}
notifyAll()使用範式:
同步代碼塊,執行結束的最後一行語句,釋放鎖之前喚醒其他爭搶這把鎖的線程。
synchronized同步鎖使用方式
- 同步代碼塊
- 同步方法
synchronized鎖的是什麼呢?
synchronized使用時需要注意以下幾點:
- synchronized鎖要設計好鎖的粒度,減少資源的損耗。
- synchronized鎖聲明的方法上,在方法取的方法錢有ANS_SYCHRONIZED標識。
- synchronized代碼塊,則反編譯會有monitor enter 和monitor exit方法作爲鎖開始和結束。jvm實現。
- synchronized是可重入鎖。
- synchronized鎖代碼區間若拋出異常則釋放鎖。
- synchronized鎖靜態方法其實鎖的是靜態方法所屬的類對象。
- synchronized鎖普通方法鎖的是這個類當前的實例對象。
- 一個類中多個方法、synchronized鎖不同的對象,不爭奪同一把鎖的方法可以並行執行。
代碼編寫
public class Weapon {
public static void main(String[] args) {
GoldAKGun akGun = new GoldAKGun("黃金AK",10);
new Thread(new Sniper(akGun),"狙擊手->順溜").start();
new Thread(new Sniper(akGun),"狙擊手->二嘎").start();
//new Thread(new Deputy(akGun),"備彈手->二雷").start();
new Thread(new Deputy(akGun),"備彈手->二毛").start();
new Thread(new Deputy(akGun),"備彈手->鐵蛋").start();
new Thread(new Deputy(akGun),"備彈手->大柱").start();
}
public static class GoldAKGun{
private String gunName = "AK"; //默認名字AK
private Integer bollet = 0; //默認備彈 0 發
public GoldAKGun(String gunName, Integer bollet) {
this.gunName = gunName;
this.bollet = bollet;
}
public synchronized void putBollet(String name){
try {
while (bollet>=20){
System.out.println(name + "->發現["+ gunName +"]的子彈是滿的 !");
this.wait();
}
}catch (InterruptedException e){
e.printStackTrace();
}
bollet++;
System.out.println(name + "->成功拿起["+ gunName +"]裝入一枚子彈,當前子彈數目:" + bollet);
this.notifyAll();
}
public synchronized void shotGirld(String name){
try {
while (bollet<=0){
System.out.println(name + "->發現["+ gunName +"]的彈夾是空的 !");
this.wait();
}
}catch (InterruptedException e){
e.printStackTrace();
}
bollet--;
System.out.println(name + "->扣動["+ gunName +"]板機,打了個鳥兒,當前子彈數目:" + bollet);
this.notifyAll();
}
}
public static class Sniper implements Runnable{
GoldAKGun ak47;
public Sniper(GoldAKGun ak47){
this.ak47 = ak47;
}
@Override
public void run(){
for (;;) {
sleep(500);
ak47.shotGirld(Thread.currentThread().getName());
}
}
}
public static class Deputy implements Runnable{
GoldAKGun ak47;
public Deputy(GoldAKGun ak47){
this.ak47 = ak47;
}
@Override
public void run(){
for (;;) {
sleep(1000);
ak47.putBollet(Thread.currentThread().getName());
}
}
}
public static void sleep(Integer millis){
try {
Thread.sleep(millis);
}catch (Exception e){
e.printStackTrace();
}
}
}
總結描述
synchronized、wait、notifyAll在常用的java開發框架中,多次出現,需要深入的研究一下。