Java线程与线程池实战

线程池核心设计与实现

Java 中的线程池核心实现类是 ThreadPoolExecutor,本文基于 JDK 1.8 的源码来分析 Java 线程池的核心设计与实现。

Executor

ThreadPoolExecutor 实现的顶层接口是 Executor

Executor 提供了一种思想:将任务提交任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供 Runnable 对象,将任务的运行逻辑提交到执行器(Executor)中,由 Executor 框架完成线程的调配和任务的执行部分。

ExecutorService

ExecutorService 接口增加了一些能力:

  • 扩展了可异步跟踪执行任务生成返回值 Future 的方法,如 submit() 等方法。
  • 提供了管控线程池生命周期的方法,如 shutDown()shutDownNow() 等。

AbstractExecutorService

AbstractExecutorService 则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。

最下层的实现类 ThreadPoolExecutor 实现最复杂的运行部分,ThreadPoolExecutor 将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

ThreadPoolExecutor 

线程池使用核心类

·corePool:核心线程池的大小。
·maximumPool:最大线程池的大小。
·BlockingQueue:用来暂时保存任务的工作队列。
·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler

线程池接口继承实现类图,最终使用ThreadPoolExecutor类来使用JDK提供的线程池功能

线程池模拟处理生产者/消费者业务模型

/**
 * TODO 模拟场景: 1000000个处理任务, 队列10000, 总计只有100个线程进行处理, 每个线程需要处理0.02秒, 模拟实现类似业务场景
 *
 * @author :seagull
 * @date :Created in 2022/10/11
 * @description:
 * @modified By:
 */
public class ThreadTest {

    public static ThreadPoolExecutor poolExecutor;

    static {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10000);
        poolExecutor = new ThreadPoolExecutor(1, 100, 5, TimeUnit.MINUTES, queue);
    }

    static class MyThread<T> extends Thread {
        T obj;

        public MyThread(T obj) {
            this.obj = obj;
        }
    }


    public static void main(String[] args) {
        for (int i = 0; i < 1000000; i++) {
            String completedMsg = "第" + (i + 1) + "个任务执行完成! ";
            MyThread<String> thread = new MyThread<String>(completedMsg) {
                @Override
                public void run() {
                    String threadName = Thread.currentThread().getName();
                    long threadId = Thread.currentThread().getId();
                    int size = poolExecutor.getQueue().size();
                    int activeCount = poolExecutor.getActiveCount();
                    System.out.printf("线程名:%s 线程id:%s 消息:%s 剩余:%s 活跃线程:%s\r\n", threadName, threadId, this.obj, size, activeCount);
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            poolExecutor.execute(thread);
            System.out.printf("第%s个任务已添加\r\n", i + 1);
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("主线程结束.");
    }

}

实际业务处理简单使用

/**
 * @author :seagull
 * @date :Created in 2022/10/11
 * @description:
 * @modified By:
 */
public class FileThreadPoolUtil {

    static ThreadPoolExecutor poolExecutor;
    static int addErrorCount = 0;

    static class MyRejectPolicy implements RejectedExecutionHandler {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            if (r instanceof FileDealThread) {
                addErrorCount++;
                FileDealThread dealThread = (FileDealThread) r;
                System.out.println(addErrorCount + ":拒绝处理任务:数据:" + dealThread.task.errorMsg);
            }
        }
    }

    static {
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(1000);
        ThreadFactory factory = new CustomizableThreadFactory("file-deal-pool-");
        poolExecutor = new ThreadPoolExecutor(3, 100, 5, TimeUnit.MINUTES, queue, factory, new MyRejectPolicy());
    }

    static class FileDealThread<T> extends Thread {
        FileDealTask task;

        public FileDealThread(FileDealTask task) {
            this.task = task;
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            long threadId = Thread.currentThread().getId();
            int size = poolExecutor.getQueue().size();
            int activeCount = poolExecutor.getActiveCount();
            System.out.printf("线程名:%s 线程id:%s 剩余:%s 活跃线程:%s\r\n", threadName, threadId, size, activeCount);
            task.run();
        }
    }

    public static abstract class FileDealTask {
        // 数据
        protected String finalImgBase64;
        // 存储文件
        protected String finalTargetFilePath;
        protected String fileName;
        String errorMsg;

        public abstract void run();

        public FileDealTask(String finalImgBase64, String finalTargetFilePath, String fileName) {
            this.finalImgBase64 = finalImgBase64;
            this.finalTargetFilePath = finalTargetFilePath;
            this.fileName = fileName;
            this.errorMsg = (finalImgBase64 + ":" + finalTargetFilePath + ":" + fileName);
        }
    }

    public static <T> void addTask(FileDealTask task) {
        poolExecutor.execute(new FileDealThread<T>(task));
    }

}
FileThreadPoolUtil.addTask(new FileThreadPoolUtil.FileDealTask("", filePathString, fileName) {
     @Override
     public void run() {
          System.out.println("缓存文件路径:" + filePathString);
     }
});

Executors

Executors类主要用于提供线程池相关的操作,它提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。

Executors的几种线程池实现
5个,分别如下:

1.newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。(线程最大并发数不可控制)

2.newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

3.newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

4.newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

5.newWorkStealingPool:jdk1.8新增,创建持有足够线程的线程池来支持给定的并行级别,并通过使用多个队列,减少竞争,它需要穿一个并行级别的参数,如果不传,则被设定为默认的CPU数量。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值