Java中线程池的实现方式

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(588)   2023-09-08 09:14:27

线程是独立程序的执行路径。在java中,每个线程都扩展了java.lang.Thread类或者实现了java.lang.Runnable。

多线程是指在一个任务中同时执行两个或多个线程。在多线程中,每个任务可以有多个线程,这些线程可以并发运行,可以是异步的,也可以是同步的。您可以在我写的关于多线程的另一篇教程此处中找到有关线程和多线程的更多信息。

1.什么是线程池

线程池代表一组执行任务的工作线程,每个线程都可以被多次重用。如果在所有线程都处于活动状态时提交了新任务,它们将在队列中等待,直到有线程可用。线程池实现在内部使用 LinkedBlockingQueue 向队列添加和删除任务。
我们通常想要的是一个工作队列与一组固定的工作线程组合,它使用 wait()notify() 来通知等待线程新工作已经到达。以下示例显示了一个简单的工作队列,它是 Runnable 对象的队列。这是调度程序和工作队列的通用约定,尽管 Thread API 没有特别需要使用 Runnable 接口。
package tutorials;

import java.util.concurrent.LinkedBlockingQueue;

public class ThreadPool {
    private final int nThreads;
    private final PoolWorker[] threads;
    private final LinkedBlockingQueue queue;

    public ThreadPool(int nThreads) {
        this.nThreads = nThreads;
        queue = new LinkedBlockingQueue();
        threads = new PoolWorker[nThreads];

        for (int i = 0; i < nThreads; i++) {
            threads[i] = new PoolWorker();
            threads[i].start();
        }
    }

    public void execute(Runnable task) {
        synchronized (queue) {
            queue.add(task);
            queue.notify();
        }
    }

    private class PoolWorker extends Thread {
        public void run() {
            Runnable task;

            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            System.out.println("An error occurred while queue is waiting: " + e.getMessage());
                        }
                    }
                    task = queue.poll();
                }

                // If we don't catch RuntimeException,
                // the pool could leak threads
                try {
                    task.run();
                } catch (RuntimeException e) {
                    System.out.println("Thread pool is interrupted due to an issue: " + e.getMessage());
                }
            }
        }
    }
}

在处理队列时使用同步块很重要,它可以控制线程对队列的访问。

package tutorials;

public class Task implements Runnable {

    private int num;

    public Task(int n) {
        num = n;
    }

    public void run() {
        System.out.println("Task " + num + " is running.");
    }
}
import tutorials.Task;
import tutorials.ThreadPool;

public class Main {

    public static void main(String[] args) {
        ThreadPool pool = new ThreadPool(7);

        for (int i = 0; i < 5; i++) {
            Task task = new Task(i);
            pool.execute(task);
        }
}

在上面的示例中,我们使用了 notify() 而不是 notifyAll()。因为 notify()notifyAll() 具有更理想的性能特征;特别是,notify() 会导致更少的上下文切换,这在服务器应用程序中很重要。但在其他情况下使用 notify() 时务必要确保使用 notify() 存在微妙的风险,并且仅在某些特定条件下才适合使用它。

下图演示了上面例子中的线程池设计。

图1。线程池设计

2.线程池的有效利用

线程池是构建多线程应用程序的强大机制,但它并非没有风险。使用线程池构建的应用程序可能具有与任何其他多线程应用程序相同的并发风险,例如死锁资源抖动、同步或并发错误、线程泄漏和请求过载

这里有几点:

  • 不要将同步等待其他任务的任务排队,因为这会导致死锁。
  • 如果任务需要等待 I/O 等资源,请指定最长等待时间,然后任务执行失败或重新排队。这保证了通过为可能成功完成的另一项任务释放线程来取得一些进展。
  • 有效地调整线程池大小,并了解线程太少或太多都会导致问题。线程池的最佳大小取决于可用处理器的数量和工作队列中任务的性质。

3.总结

线程池对于组织服务器应用程序非常有用,正确实现它以防止任何问题(例如死锁和 wait()notify() 的使用复杂性)非常重要。因此,建议考虑使用 Executor 中的 util.concurrent 类之一,例如 ThreadPoolExecutor,而不是从头编写线程池。如果需要创建线程来处理短期任务,您可以考虑使用线程池。

4.下载源码

这是一个线程池的教程,要下载源代码,请点击这里

标签2: Java教程
地址:https://www.cundage.com/article/jcg-implement-thread-pool-java.html

相关阅读

Java HashSet 教程展示了如何使用 Java HashSet 集合。 Java哈希集 HashSet 是一个不包含重复元素的集合。此类为基本操作(添加、删除、包含和大小)提供恒定时间性...
SpringApplicationBuilder 教程展示了如何使用 SpringApplicationBuilder 创建一个简单的 Spring Boot 应用程序。 春天 是用于创建企业应...
通道是继 buffers 之后 java.nio 的第二个主要新增内容,我们在之前的教程中已经详细了解了这一点。通道提供与 I/O 服务的直接连接。 通道是一种在字节缓冲区和通道另一端的实体(通...
课程大纲 Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了一个分布式的、支持多租户的全文搜索引擎,带有 HTTP Web 界面和无模式的 JSON 文档。 Elasti...
解析器是强大的工具,使用 ANTLR 可以编写可用于多种不同语言的各种解析器。 在这个完整的教程中,我们将: 解释基础:什么是解析器,它可以用来做什么 查看如何设置 ANTLR 以便在 Java...
Java 是用于开发各种桌面应用程序、Web 应用程序和移动应用程序的最流行的编程语言之一。以下文章将帮助您快速熟悉 Java 语言,并迈向 API 和云开发等更复杂的概念。 1. Java语言...
Java中的继承是指子类继承或获取父类的所有非私有属性和行为的能力。继承是面向对象编程的四大支柱之一,用于提高层次结构中类之间的代码可重用性。 在本教程中,我们将了解 Java 支持的继承类型,...
Java Message Service 是一种支持正式通信的 API,称为 网络上计算机之间的消息传递。 JMS 为支持 Java 程序的标准消息协议和消息服务提供了一个通用接口。 JMS 提...
之前,我介绍了spring 3 + hibernate 集成 示例和struts 2 hello world 示例。在本教程中,我将讨论在将 spring 框架与 struts 与 hibern...
Java 项目中的一项常见任务是将日期格式化或解析为字符串,反之亦然。解析日期意味着你有一个代表日期的字符串,例如“2017-08-3”,你想把它转换成一个代表 Java 中日期的对象,例如Ja...