内容简介
本节针对于我们常听说而不常使用的 wait 和 notify 方法做个生产者和消费者案例
效果图
多线程
目录结构
目录结构
一、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<Task> tasks; private Task task; public Worker(String name, LinkedList<Task> 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<Worker> workers = new LinkedList<>(); private LinkedList<Task> 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<Worker> getWorkers() { return workers;
} public LinkedList<Task> 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<Worker> 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 方法
更多精彩内容
架构实战篇(一):Spring Boot 整合MyBatis
架构实战篇(二):Spring Boot 整合Swagger2
架构实战篇(三):Spring Boot 整合MyBatis(二)
架构实战篇(四):Spring Boot 整合 Thymeleaf
架构实战篇(五):Spring Boot 表单验证和异常处理
架构实战篇(六):Spring Boot RestTemplate的使用
架构实战篇(七):Spring Boot Data JPA 快速入门
架构实战篇(八):Spring Boot 集成 Druid 数据源监控
关注我们
如果需要源码可以关注“IT实战联盟”公众号并留言(源码名称+邮箱),小萌看到后会联系作者发送到邮箱,也可以加入交流群和作者互撩哦~~~!
注意:本文归作者所有,未经作者允许,不得转载