1.手寫生產(chǎn)者消費(fèi)者模型
所謂生產(chǎn)者消費(fèi)者模型,可以用我們生活中的例子來(lái)類比:我去一個(gè)小攤兒買吃的,老板把已經(jīng)做好的小吃都放在擺盤上,供我挑選。那么,老板就是生產(chǎn)者;我就是消費(fèi)者;擺盤就是阻塞隊(duì)列,用來(lái)當(dāng)做生產(chǎn)與消費(fèi)的緩沖區(qū)。因此,阻塞隊(duì)列在生產(chǎn)者與消費(fèi)者模型中起著至關(guān)重要的緩沖作用。
此次先演示如何手寫阻塞隊(duì)列(也可以使用Java庫(kù)中自帶的阻塞隊(duì)列)。
手寫的阻塞隊(duì)列只實(shí)現(xiàn)最基礎(chǔ)的兩個(gè)功能:入隊(duì)和出隊(duì)。之所以叫阻塞隊(duì)列,是因?yàn)楫?dāng)隊(duì)空或者隊(duì)滿的時(shí)候,都要實(shí)現(xiàn)阻塞,直到隊(duì)中不空或不滿的時(shí)候,才會(huì)取消阻塞。
手寫阻塞隊(duì)列實(shí)現(xiàn)如下:
//阻塞隊(duì)列BlockQueue static class BlockQueue{ //該隊(duì)列用一個(gè)數(shù)組來(lái)實(shí)現(xiàn),我們讓此隊(duì)列的最大容量為10 private int[] items = new int[10]; private int head = 0; private int tail = 0; private int size = 0; private Object locker =new Object(); //入隊(duì) public void put(int item) throws InterruptedException { synchronized(locker) { while (size == items.length) { //入隊(duì)時(shí),若隊(duì)滿,阻塞 locker.wait(); } items[tail++] = item; //如果到達(dá)末尾,重回隊(duì)首(實(shí)現(xiàn)循環(huán)隊(duì)列) if (tail >= items.length) { tail = 0; } size++; locker.notify(); } } //出隊(duì) public int back() throws InterruptedException { int ret = 0; synchronized (locker) { while (size == 0) { //出隊(duì)時(shí),若隊(duì)空,阻塞 locker.wait(); } ret = items[head++]; if (head >= items.length) { head = 0; } size--; locker.notify(); } return ret; } }
用兩個(gè)線程充當(dāng)生產(chǎn)者與消費(fèi)者:
public static void main(String[] args) throws InterruptedException { BlockQueue blockQueue = new BlockQueue(); //生產(chǎn)者線程 Thread produce = new Thread(){ @Override public void run() { for(int i = 0;i<10000;++i){ try { System.out.println("生產(chǎn)了:"+i); blockQueue.put(i); } catch (InterruptedException e) { e.printStackTrace(); } } } }; produce.start(); //消費(fèi)者線程 Thread customer = new Thread(){ @Override public void run() { while (true) { try { int res = blockQueue.back(); System.out.println("消費(fèi)了:" + res); //每次消費(fèi)后等1秒,也就是生產(chǎn)的快,消費(fèi)的慢 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; customer.start(); customer.join(); produce.join(); }
結(jié)果如下:可以看到,生產(chǎn)者線程先生產(chǎn)元素,(阻塞隊(duì)列容量為10),當(dāng)隊(duì)列滿時(shí),隊(duì)列阻塞,消費(fèi)者線程消費(fèi)元素,因?yàn)橄M(fèi)的慢,所以接下來(lái)生產(chǎn)者線程由于阻塞隊(duì)列不能快速生產(chǎn),只能等待消費(fèi)者線程消費(fèi)隊(duì)列中的元素,生產(chǎn)者線程才能隨著生產(chǎn),這就是阻塞隊(duì)列的緩沖作用。
2.手寫定時(shí)器
先看一下Java包中的定時(shí)器。
下面的代碼我們通過(guò)調(diào)用timer類中的schedule方法來(lái)實(shí)現(xiàn)定時(shí)器功能。schedule方法有兩個(gè)參數(shù),第一個(gè)參數(shù):要執(zhí)行的任務(wù),第二個(gè)參數(shù):時(shí)間。
下面的代碼中,schedule方法中的第一個(gè)任務(wù)參數(shù):我們創(chuàng)建了一個(gè)TimerTask實(shí)例;重寫里面的run方法來(lái)打印"觸發(fā)定時(shí)器"這句話。第二個(gè)參數(shù):3000;表示3秒后執(zhí)行這個(gè)任務(wù)。
import java.util.Timer; import java.util.TimerTask; public class Test{ public static void main(String[] args) { Timer timer = new Timer(); System.out.println("代碼開始執(zhí)行"); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("觸發(fā)定時(shí)器"); } },3000); } }
結(jié)果如下:
從上面就可以看出來(lái)我們手寫定時(shí)器需要實(shí)現(xiàn)以下兩個(gè)方面:
1.一個(gè)Task類,用來(lái)描述要實(shí)現(xiàn)的任務(wù)
2.一個(gè)Timer類,類中再實(shí)現(xiàn)一個(gè)schedule方法
Task類實(shí)現(xiàn)
//Task類用來(lái)描述任務(wù),它繼承Comparable接口是因?yàn)橐獙⑷蝿?wù)放到優(yōu)先級(jí)阻塞隊(duì)列中 static class Task implements Comparable<Task>{ //command表示這個(gè)任務(wù)是什么 private Runnable command; //time是一個(gè)時(shí)間戳 private long time; public Task(Runnable command,long time){ this.command = command; this.time = System.currentTimeMillis()+time; } public void run(){ command.run(); } //因?yàn)橐獙ask任務(wù)放到優(yōu)先級(jí)阻塞隊(duì)列中,所以要重寫compareTo方法,我們將時(shí)間短的任務(wù)放到隊(duì)頭 @Override public int compareTo(Task o) { return (int)(this.time - o.time); } }
Timer類實(shí)現(xiàn)
//Timer類中需要有一個(gè)定時(shí)器,還需要有一個(gè)schedule方法 static class Timer{ //使用優(yōu)先級(jí)阻塞隊(duì)列來(lái)放這些任務(wù),這樣才能把最接近時(shí)鐘的任務(wù)放到隊(duì)頭,我們每次掃描隊(duì)頭任務(wù)就行了 private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>(); //locker用來(lái)解決忙等問(wèn)題 private Object locker = new Object(); //構(gòu)造方法中完成定時(shí)器功能 public Timer(){ //需要構(gòu)造一個(gè)線程,來(lái)不斷地掃描隊(duì)頭,來(lái)判斷隊(duì)頭任務(wù)是否到點(diǎn),也就是是否該開始執(zhí)行了 Thread t = new Thread(){ @Override public void run() { while(true){ //取出隊(duì)首任務(wù)來(lái)判斷是否到時(shí)間了 try { Task task = queue.take(); long current = System.currentTimeMillis(); //當(dāng)前時(shí)間戳小于時(shí)鐘時(shí)間戳,表明時(shí)間還沒(méi)到,那就等待 if (current < task.time){ queue.put(task); synchronized (locker){ locker.wait(task.time-current); } }else{ //否則時(shí)間到,開始執(zhí)行任務(wù) task.run(); } } catch (InterruptedException e) { e.printStackTrace(); break; } } } }; t.start(); } //schedule方法的兩個(gè)參數(shù),command為任務(wù),delay為一個(gè)時(shí)間差例如:3000(單位為毫秒) public void schedule(Runnable command,long delay){ Task task = new Task(command,delay); queue.put(task); synchronized (locker){ locker.notify(); } } }
主線程
public static void main(String[] args) { System.out.println("程序啟動(dòng)"); Timer timer = new Timer(); timer.schedule(new Runnable() { @Override public void run() { System.out.println("觸發(fā)定時(shí)器"); } },3000);//3000表示定時(shí)時(shí)間為3秒 }
結(jié)果如下:“程序啟動(dòng)” 在程序啟動(dòng)是立刻顯示出來(lái);“觸發(fā)定時(shí)器”在3秒后顯示出來(lái)。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注服務(wù)器之家的更多內(nèi)容!
原文鏈接:https://blog.csdn.net/m0_52373742/article/details/120813402