文章目录
【Java设计模式】领导者 - 追随者模式
一、概述
领导者 - 追随者模式的目的是有效地管理一组工作线程,使多个线程能够轮流共享一组事件源,与每个源使用一个线程的方法相比,优化了资源利用率并提高了性能。
二、详细解释及实际示例
- 实际示例:
- 想象一下管理一个繁忙的餐厅,有多个服务员和一个主机。主机充当“领导者”,负责迎接客人、管理等候名单和安排客人就座。一旦客人就座,主机返回入口处管理新的到来。服务员,或“追随者”,等待主机为他们分配桌子。这种分配是轮流进行的,下一个可用的服务员接待下一组客人。这个系统确保主机有效地处理客人的流入,而服务员专注于提供服务,类似于领导者 - 追随者模式在软件系统中管理线程和任务。这种方法优化了资源利用(在这种情况下,是员工),并确保在高峰时段的顺利运行,就像它在计算环境中优化线程使用一样。
- 通俗解释:
- 领导者 - 追随者设计模式利用一个领导者线程在多个追随者线程之间分配工作,有效地管理任务委派、线程同步,并在并发编程中提高资源效率。
三、Java中领导者 - 追随者模式的编程示例
领导者 - 追随者模式是一种并发设计模式,其中一个线程(领导者)等待工作到达,进行解复用、分派和处理工作,从而增强CPU缓存亲和性并减少事件分派延迟。一旦领导者完成工作处理,它会提升一个追随者线程成为新的领导者。这种模式对于增强CPU缓存亲和性、最小化锁定开销和减少事件分派延迟非常有用。
在提供的代码中,我们有一个WorkCenter
类,它管理一组Worker
线程。其中一个工作线程被指定为领导者,负责接收和处理任务。一旦任务处理完成,领导者会从剩余的工作线程中提升一个新的领导者。
// WorkCenter类
public class WorkCenter {
@Getter
private Worker leader;
private final List<Worker> workers = new CopyOnWriteArrayList<>();
// 创建工作线程并设置初始领导者的方法
public void createWorkers(int numberOfWorkers, TaskSet taskSet, TaskHandler taskHandler) {
for (var id = 1; id <= numberOfWorkers; id++) {
var worker = new Worker(id, this, taskSet, taskHandler);
workers.add(worker);
}
promoteLeader();
}
// 提升新领导者的方法
public void promoteLeader() {
Worker leader = null;
if (!workers.isEmpty()) {
leader = workers.get(0);
}
this.leader = leader;
}
}
在Worker
类中,每个工作线程都是一个线程,等待任务进行处理。如果工作线程是领导者,它会处理任务,然后提升一个新的领导者。
// Worker类
public class Worker implements Runnable {
private final long id;
private final WorkCenter workCenter;
private final TaskSet taskSet;
private final TaskHandler taskHandler;
@Override
public void run() {
while (!Thread.interrupted()) {
try {
if (workCenter.getLeader()!= null &&!workCenter.getLeader().equals(this)) {
synchronized (workCenter) {
if (workCenter.getLeader()!= null &&!workCenter.getLeader().equals(this)) {
workCenter.wait();
continue;
}
}
}
final Task task = taskSet.getTask();
synchronized (workCenter) {
workCenter.removeWorker(this);
workCenter.promoteLeader();
workCenter.notifyAll();
}
taskHandler.handleTask(task);
workCenter.addWorker(this);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
在App
类中,我们创建一个WorkCenter
,向TaskSet
添加任务,然后启动工作线程。领导者工作线程将开始处理任务,一旦完成一个任务,它将提升一个新的领导者。
// App类
public class App {
public static void main(String[] args) throws InterruptedException {
var taskSet = new TaskSet();
var taskHandler = new TaskHandler();
var workCenter = new WorkCenter();
workCenter.createWorkers(4, taskSet, taskHandler);
addTasks(taskSet);
startWorkers(workCenter);
}
private static void addTasks(TaskSet taskSet) throws InterruptedException {
var rand = new SecureRandom();
for (var i = 0; i < 5; i++) {
var time = Math.abs(rand.nextInt(1000));
taskSet.addTask(new Task(time));
}
}
private static void startWorkers(WorkCenter workCenter) throws InterruptedException {
var workers = workCenter.getWorkers();
var exec = Executors.newFixedThreadPool(workers.size());
workers.forEach(exec::submit);
exec.awaitTermination(2, TimeUnit.SECONDS);
exec.shutdownNow();
}
}
这是领导者 - 追随者模式的基本示例。领导者工作线程处理任务,并在完成一个任务后提升一个新的领导者。新的领导者然后开始处理下一个任务,循环继续。
四、何时在Java中使用领导者 - 追随者模式
- 领导者 - 追随者模式在需要在单个线程上高效处理多个服务、避免资源冲突并提高并发编程环境中可扩展性的场景中非常有用。
- 适用于服务器环境,其中必须以最小的资源消耗同时处理多个客户端请求。
五、领导者 - 追随者模式在Java中的实际应用
- 处理多个传入连接的网络服务器。
- 管理大量输入/输出源的事件驱动应用程序。
六、领导者 - 追随者模式的好处和权衡
好处:
- 减少线程数量和上下文切换,从而提高性能并降低资源利用率。
- 提高系统的可扩展性和响应能力。
权衡:
- 管理领导者和追随者之间的同步增加了复杂性。
- 如果实现不正确,可能会导致资源利用率不足。