關于多執行緒的學習(下)
提到多執行緒,那必然提到經典題目——生產者消費者模型,使用多執行緒的方式模擬一個簡單的生產者消費者模型,在僅考慮生產和消費輪流執行的情況下,即每次生產后必定進行消費,二者交替執行,且每次購買數量無法超過商品的存盤,商品的總數不得超過一千,
首先需要定義一個物件表示商品:
public class Commodity {
//用于判斷是生產還是消費
public boolean flag=true;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
//總容量
private static int num=0;
}
再定義一個生產者:
public class creatDemo implements Runnable {
Commodity commodity;
public creatDemo(Commodity commodity) {
this.commodity = commodity;
}
public void run() {
while (true) {
synchronized (commodity) {
//如果flag是false,讓當前執行緒進入等待
if (!commodity.flag) {
try {
commodity.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果是true,進行生產
if (commodity.flag) {
//保證生產后總數不超過1000
int count = (int) Math.floor(Math.random() * (1000 - commodity.getNum()) + 1);
commodity.setNum(count + commodity.getNum());
System.out.println("本次生產" + count + ",總共還有" + commodity.getNum());
//將flag變為false
commodity.flag=false;
//喚醒執行緒池中沉睡的執行緒,開啟下一次資源搶奪,但實際下
// 次要執行的操作早就確定了,這就是黑幕
commodity.notify();
}
}
}
}
}
我們再創建一個消費者:
public class ShopDemo implements Runnable {
Commodity commodity;
public ShopDemo(Commodity commodity) {
this.commodity = commodity;
}
public void run() {
while (true) {
synchronized (commodity) {
//如果是true,進入等待
if (commodity.flag){
try {
commodity.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果是false,進行消費操作
if (!commodity.flag) {
int count = (int) Math.floor(Math.random() * (commodity.getNum()) + 1);
commodity.setNum(commodity.getNum() - count);
System.out.println("本次購買" + count+"剩余"+commodity.getNum());
commodity.flag=true;
commodity.notify();
}
}
}
}
}
最后創建執行緒
public class test {
public static void main(String[] args) {
Commodity c=new Commodity();
//使用匿名物件來直接構造
new Thread(new creatDemo(c)).start();
new Thread(new ShopDemo(c)).start();
}
}
這樣我們就實作了一個簡單的消費者生產者模式,再此基礎上我們可以不斷添加其他的條件,例如只有商品數量為0時才會再去生產,或者生產和消費同時進行等其他操作,
總結:
- 執行緒的等待和喚醒機制可以人為控制執行緒之間的爭奪資源(俗稱暗箱操作),我們可以使用wait()將該執行緒進入等待狀態,此時如果不進行notify()是無法繼續執行該執行緒的,
- notify會隨機喚醒執行緒池中的一條執行緒,而不是指定喚醒某條執行緒,
補充:
sleep和wait
1.sleep需要指定休眠時間,如果執行緒沒有鎖,那么會釋放執行權,如果執行緒有鎖,那么不釋放執行權,這個方法是設計在Thread類上,是一個靜態方法,可以使用Thread.sleep(0)讓執行緒重新爭奪資源,
2.wait可以指定也可以不指定時間,如果不指定時間需要進行喚醒,釋放執行權也釋放鎖,這個方法是設計在Object上,是一個普通方法,如果指定了時間,也仍需被其他執行緒喚醒,
守護執行緒
守護執行緒用于守護被守護執行緒,如果被守護的執行緒結束,那么守護執行緒隨之結束,
如果一個執行緒不是守護執行緒,那么就是被守護執行緒,如果所有的被守護執行緒結束,那么守護執行緒也會隨之結束,最常見的守護執行緒就是GC,
執行緒的狀態
創建、就緒、執行、阻塞、消亡
HashTable和ConcurrentHashMap的區別(僅執行緒安全)
HashTable是同步執行緒安全的,原因是它在底層為整個表加上了鎖,一個大鎖將整個表鎖住,所以是同步的,但是效率極低,
HashMap雖然效率高,但是執行緒過于不安全,所以我們使用ConcurrentHashMap,它也是給執行緒加鎖,但是它不是加一個大鎖,JDK1.7是由Segment陣列實作的,一個Segment鎖住幾個HashEntry元素;JDK1.8是由synchronized鎖住transient volatile Node<K,V>[] table 陣列的一個元素(即頭結點,鎖粒度比JDK1.7低),還通過CAS(Unsafe物件)操作資料更新、插入等,(大致意思是進行操作時是對每個桶進行加鎖,這樣其他執行緒在進行非本鏈表的操作時不會受影響,JDK1.7之前是直接對segment加鎖,1.8之后放棄了segment,對節點頭加鎖,這樣消耗的資源會少,從而提高效率)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/276321.html
標籤:其他
上一篇:水杯如何測驗 (測驗用例)
下一篇:記錄一次XSS(跨站腳本攻擊)
