Java线程_07_线程池

本文深入探讨了Java线程池的原理、构建及优化方法,包括吞吐量、资源利用率、任务队列和线程池大小的合理设置,以及如何避免并发问题和资源泄露。通过实例代码展示了线程池的基础结构和工作流程,旨在帮助开发者提高程序性能和资源管理效率。

关键词

吞吐量、资源利用率、大量同类型的任务、线程池、工作线程、任务队列

应用场景

  1. 最佳资源利用率
  2. 程序吞哇量
  1. 任务明确但数量多
  2. thread创建和管理的成本
  1. 合理构建程序结构,封装对同一类型事物的处理

这是有风险的

  1. 资源都被池给占用了
  2. 工作线程并发错误
  1. 工作线程异常产生泄漏
  1. 工作线程时限
  1. 被大量的请求压垮

主动权在你,你会考虑?

  1. 考虑和避免任务间的并发问题
  1. 考虑工作线程加上时限处理逻辑
  1. 考虑合理的线程池大小
  2. 考虑合理的队列大小
  1. 考虑CPU是几个核
  1. 对客户要执行任务进行归类
  1. 这不是一个池子,这可以是多个
  1. 考虑线程所控制的资源
    1. 自身资源
    1. 所管理的资源
      1. JDBC
      2. 套接字
      3. 文件
  1. 耗时多?
    1. 是创建和管理线程生命周期的时间
    1. 还是真实任务处理时间

线程池基本结构

  1. 任务队列
  1. 工作线程集合
  1. 队列和集合的管理逻辑
    1. 控制队列的大小
    2. 控制工作线程集合的大小
    3. 管理工作线程生命周期
    1. 控制工作线程执行任务
      1. 任务逻辑异常
      1. 任务耗时问题
      1. 任务并发引用
      1. 避免错误
    1. 死锁
      1. 资源过载
      1. 并发问题

线程池基础

/**
 * 线程池基础
 * @author WangYanCheng
 * @version 2012-11-31
 */
public class ThreadPool_01 {
    // 有一个任务队列
    private final LinkedList<Runnable> queue;
    // 有一个工作线程数量
    private final int workerThreadNums;
    // 有一个工作线程集合
    private final ThreadWorker[] threadWorkers;
    // 实际工作线程数量
    private int thNums;

    /**
     * Constructor
     * @param workerThreadNums 初始工作线程数量
     */
    public ThreadPool_01(int workerThreadNums) {
        this.queue = new LinkedList<Runnable>();
        this.workerThreadNums = workerThreadNums;
        this.threadWorkers = new ThreadWorker[this.workerThreadNums];
        for (ThreadWorker tw : this.threadWorkers) {
            tw = new ThreadWorker(ThreadWorker.THNAMEPREFIX.concat(String.valueOf(thNums++)));
            tw.start();
        }
    }

    /**
     * 提供给客户端的API,负责接收逻辑任务,并调度执行
     * @param r 可执行任务
     */
    public void execute(Runnable r) {
        synchronized (queue) {
            queue.add(r);
            // 不存在共享资源的模式,只通知一个等待线程即可
            queue.notify();
        }
    }

    /**
     * 线程池中的工作线程,其生命周期被池对象完全控制,负责执行任务
     * @author WangYanCheng
     * @version 2012-11-31
     */
    private class ThreadWorker extends Thread {
        private static final String THNAMEPREFIX = "TW_";

        /**
         * Constructor
         * @param thName
         */
        public ThreadWorker(String thName) {
            super(thName);
        }
        @Override
        public void run() {
            Runnable run;
            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    run = queue.removeFirst();
                }
                // 任务逻辑处理异常一定不要影响到基础线程
                try {
                    run.run();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 充当客户端,负责构造可执行的任务
     * @author WangYanCheng
     * @version 2012-11-31
     */
    private class ThreadTester extends Thread {
        public ThreadTester() {
            super();
            setName("ThreadTester");
            start();
        }

        @Override
        public void run() {
            int taskCount = 1;
            while (true) {
                execute(new VirtualTask(VirtualTask.TASKPREFIX.concat(String.valueOf(taskCount++))));
                try {
                    sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 任务实例
     * @author WangYanCheng
     * @version 2012-11-31
     */
    private class VirtualTask implements Runnable {
        private static final String TASKPREFIX = "CT_";
        /** 任务Id */
        private String taskId;

        /**
         * Constructor
         * @param taskId 任务Id
         */
        public VirtualTask(String taskId) {
            this.taskId = taskId;
        }

        /**
         * 任务逻辑入口
         */
        public void run() {
            System.out.println(this);
            try {
                Thread.sleep((long)(Math.random() * 5000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return "VirtualTask [taskId=" + taskId + "]";
        }
    }

    /**
     * 测试入口
     * @param args 参数列表
     */
    public static void main(String[] args) {
        ThreadPool_01 tp01 = new ThreadPool_01(2);
        tp01.new ThreadTester();
    }
}


思维导图_Java_线程池

资料

  1. Java Thread 3th
  2. http://www.ibm.com/developerworks/cn/java/j-jtp0730/

后续

  1. Java#Thread Pool支持

### Java 线程池大小设置的最佳实践 在设计和实现基于线程池的应用程序时,合理设置线程池的大小对于系统的性能至关重要。以下是关于如何正确设置 Java 线程池大小的一些最佳实践: #### 1. 考虑 CPU 密集型任务 如果应用程序主要执行的是 CPU 密集型任务,则应将线程池的大小设置为 `CPU 核心数` 或稍大一些。这是因为过多的线程会引发上下文切换开销,从而降低整体效率。 计算公式如下: \[ \text{线程池大小} = \text{可用处理器数量} + 1 \] 例如,在一台拥有 8 核 CPU 的服务器上,可以考虑将线程池大小设置为 9 左右[^1]。 #### 2. 针对 I/O 密集型任务 当任务涉及大量的输入/输出操作(如文件读写、网络通信等)时,由于这些任务通常会让线程处于等待状态,因此可以增加线程的数量以提高吞吐量。 一种常见的经验法则为: \[ \text{线程池大小} = (\text{核心数} \times (1 + W/C)) \] 其中 \(W\) 是等待时间,\(C\) 是实际工作时间[^3]。 假设每项任务有 90% 的时间用于等待数据传输完成,而仅花费 10% 的时间进行有效运算,那么理论上可创建多达十倍于物理核数的线程数目。 #### 3. 动态调整策略 考虑到不同环境下的负载变化可能很大,静态设定固定数值未必总是最优解法;此时采用动态调节机制便显得尤为重要——即依据实时监控到的各项指标自动增减活动中的工作者线程实例总数目。这可以通过自定义扩展 ThreadPoolExecutor 类并重载其相应方法来达成目标[^2]。 #### 4. 使用合适的队列长度 除了控制最大允许并发执行的工作单元外,还需要注意待处理请求排队等候区域容量限制问题。过长的任务序列可能导致内存耗尽风险加剧;反之则容易造成资源闲置浪费现象发生。故此需综合考量具体应用场景需求后再做决定。 ```java import java.util.concurrent.*; public class CustomThreadPool { public static void main(String[] args) throws InterruptedException { int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; int maximumPoolSize = corePoolSize + 1; ExecutorService executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadFactoryBuilder().setNameFormat("custom-thread-%d").build(), new ThreadPoolExecutor.AbortPolicy() ); // Submit tasks here... executor.shutdown(); } } ``` 上述代码片段展示了如何通过 ThreadPoolExecutor 构造函数显式指定多个重要属性值,包括但不限于初始启动的核心线程计数、最高许可界限以及超出部分该如何处置等等细节方面内容。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值