博客
关于我
手写线程池
阅读量:759 次
发布时间:2019-03-21

本文共 4764 字,大约阅读时间需要 15 分钟。

理解线程池以及实现一个简单的线程池

为什么需要线程池

我们不必一味向线程池看齐,但是理解其背后的原理有助于更好地利用资源。在没有线程池的情况下,系统会经常因为无限创建线程而崩溃。每个线程都需要资源支持,就像吃火锅时需要长凳一样,线程池类似于长凳,协调大量订单="/">线程池的工作机制

线程池的工作原理

核实海底捞的场景,这部分已经理解完毕。现在让我们转而讨论线程池的工作机制。实际上,线程池在底层是一个无差错的队列系统。每次新的请求到达时,通过 ticket 块塞队列方式下单,这些被称为 tasks 的工作单元会被分配给线程池中的工作线程。此处的关键是理解如何有效地管理这些请求。

在我们的一个简单的线程池中,队列被设置为我们步骤中提到的容量大小,而线程被启动成个数固定的池。

初始设置 存储要执行的任务,块 创建固定数量的线程,我们称它们为 worker

实现线程池的元素

如果你决心自己动手写一个线程池,你需要了解其中的这些关键部分。 存储你的任务,使用-linked hash set或者其他的队列组件。这里我们使用iso Nutzungios e edek久 ,我们需要一个阻塞队列。选择一个队列实现是关键性的一步

接下来,我们需要管理员工的数量。每个员工就会成为一个 thread。他们需要一直保持运行状态,直到他们从队列获得任务进行处理。在glassDoor例子中,可以看到我们的worker 线程就拿着pool作为背景语境。

这部分讨论略大,分类整理网用,包括以下几个关键点:

  • 队列:用来存储需要执行的任务。我们可以采用阻塞队列。
  • 工作人员:采用池化的线程,这些线程便利地实现任务处理.
  • 线程间的协调和管理.

这是一个高效的线程池输入,采用 pool统一管理线程。

开始编码

在编写代码之前,让我们理清需求。我们的线程池需要处理什么样的任务序列?在我们的例子中,任务由一个Runnable实现。每次提交任务会被放置到队列中。线程池中的 worker 线程会在空闲时就从队列中获取任务并运行。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class MyThreadPool {
// 队列容量,设置为阻塞队列形式
private final BlockingQueue
blockingQueue = new LinkedBlockingDeque<>();
// 存储工作线程,保持其处于就绪状态
private final List
workers = new ArrayList<>();
// 线程池的状态标志
private volatile boolean isRunning = true;
public MyThreadPool(int taskQueueSize, int workerCount) {
if (taskQueueSize <= 0 || workerCount <= 0) {
throw new IllegalArgumentException("Task queue and worker count must be at least 1");
}
// 初始化队列
this.blockingQueue = new LinkedBlockingDeque<>(taskQueueSize);
// 初始化工作线程
this.workers = new ArrayList<>();
for (int i = 0; i < workerCount; i++) {
Thread worker = new Thread() {
@Override
public void run() {
Runnable task = null;
try {
while (isRunning || !blockingQueue.isEmpty()) {
if (isRunning) {
task = blockingQueue.take();
} else {
task = blockingQueue.poll();
}
}
if (task != null) {
task.run();
System.out.println("Worker thread: " + Thread.currentThread().getName() + " executed task");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
worker.start();
this.workers.add(worker);
}
}
// 提交要执行的任务
public boolean submit(Runnable task) {
if (!isRunning) {
return false;
}
if (blockingQueue.size() < this.blockingQueue.remainingCapacity()) {
return this.blockingQueue.offer(task);
} else {
return false;
}
}
// 关闭线程池
public void shutDown() {
this.isRunning = false;
for (Thread worker : workers) {
if (worker.getState() == Thread.State.BLOCKED) {
worker.interrupt();
}
}
}
}

代码解释

1 noises// 队列设置最大的容量大小是 taskQueueSize,这样可以避免队列过载 线程池的所有线程都是预先创建好的,保持在就绪状态,这样可以在遇到任务时立即分配处理。而非每次线程创建时就启动线程(A therapist的思路可能会引发线程数量激增的问题)

2 submit方法的作用就是将一个新的 runnable 任务加入队列中。针对非空余量的队列来说,才能成功提交任务。我们需要注意容量限制。

3 shutDown方法的动作是停止线程池继续运行,并对正在等待任务的外部工作人员发出中断信号。这是必要的操作,因为我们希望线程池能够干净地停止当前的处理过程,而不是等待堆积的任务无法完成。

测试与结果

让我们用代码测试一下线程池的性能与正确工作。

public class MyThreadPoolDemo {
public static void main(String[] args) {
MyThreadPool pool = new MyThreadPool(3,3);
for (int i = 0; i < 3; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("## Task #" + i);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
pool.shutDown();
}
}

分析测试结果

在这个测试中,我们定义了一个即使线程池的大小为3,那么最多同时执行三次。每个任务都会被提交到队列中。在提交后设置500毫秒的等待时间,这能观察到线程池的行为。

可以详细分析每一步的输出:

  • 第一个任务提交,打印“## Task #0”,此时线程池有一个任务在队列中
  • 第二个任务提交,打印“## Task #1”,两个任务在队列中
  • 第三个任务提交,打印“## Task #2”,三项任务全部在队列中
  • 然后,第五个百米,在每次提交的时候,我们会让线程进入睡眠状态。我们发现在这段时间内,如果线程已经存在了,它就不会被打断,直到线程池中的工作人员从队列里拿来任务进行处理。

    之后,在 pool.shutDown() 被调用,线程池中的所有工作人员都会被停止处理。现在的关键问题是,工作人员是正确地完成它们运袅并退出。

    通过运行这个程序,你们将注意到线程池中的每个工作线程只会处理到它被==-d或队列不再有任务时停止。而线程池的情况下,调用 shutDown() 后会使线程发散停止处理。

    这种方式确保了在线程池关闭后,任何使得工作线程处于等待状态的任务也能得到中断处理,从而线程安全地退出。

    总结

    在这段时间的学习中,我们理解了线程池的重要性,它如何解决了线程资源管理的难题。创建一个简单的线程池,能够给我们带来关于线程管理的直接练习。通过测试代码,我们看到线程池在操作中是那么高效和自然。线程池似乎在处理任务时远超过线性速度的预期,从而成为我们日常编程中必不可少的工具。

    转载地址:http://gxygz.baihongyu.com/

    你可能感兴趣的文章
    Objective-C实现conjugate gradient共轭梯度算法(附完整源码)
    查看>>
    Objective-C实现coulombs law库仑定律算法(附完整源码)
    查看>>
    Objective-C实现currency converter货币换算算法(附完整源码)
    查看>>
    Objective-C实现data transformations数据转换算法(附完整源码)
    查看>>
    Objective-C实现DBSCAN聚类算法(附完整源码)
    查看>>
    Objective-C实现DBSCAN聚类算法(附完整源码)
    查看>>
    Objective-C实现degreeToRadian度到弧度算法(附完整源码)
    查看>>
    Objective-C实现depth first search深度优先搜索算法(附完整源码)
    查看>>
    Objective-C实现des文件加密算法(附完整源码)
    查看>>
    Objective-C实现Diffie-Hellman算法(附完整源码)
    查看>>
    Objective-C实现Dijkstra最小路径算法(附完整源码)
    查看>>
    Objective-C实现Dijkstra迪杰斯特拉算法(附完整源码)
    查看>>
    Objective-C实现dijkstra银行家算法(附完整源码)
    查看>>
    Objective-C实现Dinic算法(附完整源码)
    查看>>
    Objective-C实现disjoint set不相交集算法(附完整源码)
    查看>>
    Objective-C实现DisjointSet并查集的算法(附完整源码)
    查看>>
    Objective-C实现djb2哈希算法(附完整源码)
    查看>>
    Objective-C实现DNF排序算法(附完整源码)
    查看>>
    Objective-C实现doomsday末日算法(附完整源码)
    查看>>
    Objective-C实现double factorial iterative双阶乘迭代算法(附完整源码)
    查看>>