JavaWeb實戰篇:多線程Wait、NotifyAll案例

JavaWeb實戰篇:多線程Wait、NotifyAll案例

內容簡介

本節針對於我們常聽說而不常使用的 wait 和 notify 方法做個生產者和消費者案例

效果圖

JavaWeb實戰篇:多線程Wait、NotifyAll案例

多線程

目錄結構

JavaWeb實戰篇:多線程Wait、NotifyAll案例

目錄結構

一、Task 負責封裝任務

package com.itunion.model;/**
* 任務

*/public class Task {
private String name; public Task(String name) { this.name = name;
} // 省略 get set}

任務對象主要負責數據參數的產地

二、Worker 負責消費任務

package com.itunion.model;import java.util.LinkedList;import java.util.Random;/**
* 任務消費者
*/public class Worker extends Thread { private LinkedList tasks; private Task task; public Worker(String name, LinkedList tasks) { super(name); this.tasks = tasks;
} @Override
public void run() { try { // 重複執行的任務
while (true) { // 對任務隊列加鎖
synchronized (tasks) { // 如果沒有任務就等待, 這裡要用while 循環而不是 if 判斷
while (tasks.isEmpty()) { // 等待任務
tasks.wait();
} // 獲取任務
task = tasks.pop(); // 反饋通知
tasks.notifyAll();
} // 執行任務 注意:執行任務不要放在同步塊裡面,那樣的話任務隊列會等到當前線程執行完之後才能拿到鎖
System.out.println(getName() + " 執行任務: " + task.getName()); // 模擬執行時間
int workTime = new Random().nextInt(2000);
Thread.sleep(workTime); // 執行完畢
System.out.println(getName() + " 執行任務: " + task.getName() + "完畢 用時:" + workTime);
}
} catch (Exception e) {
e.printStackTrace();
}
} public Task getTask() { return task;
}

}

三、Master 負責管理任務和消費者

package com.itunion.model;import java.util.LinkedList;/**
* 生產者
* @author qiao
* @version 2018/5/14
*/public class Master extends Thread { private LinkedList workers = new LinkedList<>(); private LinkedList tasks = new LinkedList<>(); public Master(int count) { super("生產者"); for (int i = 0; i < count; i++) {
workers.add(new Worker("消費者 " + i, tasks));
} // 啟動所有消費者
for (Worker worker : workers) {
worker.start();
}
} /**
* 帶阻塞的任務提交
* @param task
*/
public void submit(Task task) { synchronized (tasks) { while (tasks.size() == workers.size()) { try {
tasks.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
tasks.add(task);
tasks.notifyAll();
}
} /**
* 提交任務
* @param task
*/
public void addTask(Task task) { synchronized (tasks) {
tasks.add(task);
tasks.notifyAll();
}
} /**
* 等待所有工作完畢
*/
public void waitWorks() { synchronized (workers) { boolean hasWork = false; for (Worker worker : workers) { if (worker.getState() == State.RUNNABLE) {

hasWork = true; break;
}
} while (hasWork) { try {
workers.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public LinkedList getWorkers() { return workers;
} public LinkedList getTasks() { return tasks;
}
}

四、Dashboard 負責圖形化

package com.itunion.model;import javax.swing.*;import java.awt.*;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import java.text.SimpleDateFormat;import java.util.Date;import java.util.LinkedList;/**
* 控制檯
*/public class Dashboard extends JPanel { private Master master; private int i = 0; public Dashboard() throws HeadlessException, InterruptedException { super();
master = new Master(20); // 每次點擊增加一個任務
addMouseListener(new MouseAdapter() { @Override
public void mouseClicked(MouseEvent e) { for (int j = 0; j < 10; j++) {
master.addTask(new Task("任務" + i));
i++;
}
}
});
} @Override
public void paint(Graphics g) { super.paint(g); int i = 0, x = 10, y = 20;

y = 30 * i + 20;
g.drawString(new SimpleDateFormat("提示:點擊任意位置增加任務").format(new Date()), x, y + 15);
i++;

y = 30 * i + 20;
g.drawString(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), x, y + 15);
i++;

y = 30 * i + 20;
g.drawString("隊列待消費任務總數:" + master.getTasks().size(), x, y + 15);
i++;

LinkedList workers = master.getWorkers(); int row = 0; for (Worker worker : workers) {
i = row == 10 ? 3 : i;
x = row < 10 ? 10 : 180;
y = 30 * i + 20; boolean isWait = worker.getState() == Thread.State.WAITING;
g.setColor(isWait ? Color.GREEN : Color.YELLOW);
g.fillRect(x, y, 150, 20);
g.setColor(Color.BLACK); if (isWait || worker.getTask() == null) {
g.drawString(worker.getName() + " > 等待", x, y + 15);
} else {
g.drawString(worker.getName() + " > 執行" + worker.getTask().getName(), x, y + 15);
}
row++;
i++;
} // 重新繪製
repaint(); try { // 刷新頻率
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

五、Main 程序的入口

package com.itunion.model;import javax.swing.*;import java.awt.*;public class Main { public static void main(String[] args) throws InterruptedException {
JFrame jFrame = new JFrame();
jFrame.add(new Dashboard(), BorderLayout.CENTER);
jFrame.setSize(400, 600);
jFrame.setLocationRelativeTo(null);// 居中
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}

運行 main 方法後點擊窗體任意位置添加任務

總結

我們常用的Thread.sleep() 是線程的休眠,而wait 是Object 的方法,更多的是 資源的等待,不能直接調用線程的 wait 方法

關注我們

如果需要源碼可以關注“IT實戰聯盟”公眾號並留言(源碼名稱+郵箱),小萌看到後會聯繫作者發送到郵箱,也可以加入交流群和作者互撩哦~~~!


分享到:


相關文章: