文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java线程池大小的设置方法实例

2024-04-02 19:55

关注

Java 中线程池创建的几种方式

首先我们要先知道 Java 中创建线程池的方式,java中创建线程池的方式一般有两种,如下所示:

🐱‍🏍Executors 工厂方法创建

上代码:


package com.base.demo.design.play;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        // 创建使用单个线程的线程池
        ExecutorService es1 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            es1.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务");
                }
            });
        }

        // 创建使用固定线程数的线程池
        ExecutorService es2 = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            es2.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务");
                }
            });
        }

        // 创建一个会根据需要创建新线程的线程池
        ExecutorService es3 = Executors.newCachedThreadPool();
        for (int i = 0; i < 20; i++) {
            es3.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务");
                }
            });
        }

        // 创建拥有固定线程数量的定时线程任务的线程池
        ScheduledExecutorService es4 = Executors.newScheduledThreadPool(2);
        System.out.println("时间:" + System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            es4.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("时间:"+System.currentTimeMillis()+"--"+Thread.currentThread().getName() + "正在执行任务");
                }
            },3, TimeUnit.SECONDS);
        }

        // 创建只有一个线程的定时线程任务的线程池
        ScheduledExecutorService es5 = Executors.newSingleThreadScheduledExecutor();
        System.out.println("时间:" + System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            es5.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("时间:"+System.currentTimeMillis()+"--"+Thread.currentThread().getName() + "正在执行任务");
                }
            },3, TimeUnit.SECONDS);
        }
    }

}

运行结果如下:

pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-1-thread-1正在执行任务
pool-2-thread-1正在执行任务
pool-2-thread-2正在执行任务
pool-2-thread-1正在执行任务
pool-2-thread-3正在执行任务
pool-2-thread-2正在执行任务
pool-2-thread-3正在执行任务
pool-2-thread-1正在执行任务
pool-2-thread-3正在执行任务
pool-2-thread-2正在执行任务
pool-2-thread-1正在执行任务
pool-3-thread-1正在执行任务
pool-3-thread-2正在执行任务
pool-3-thread-2正在执行任务
pool-3-thread-3正在执行任务
pool-3-thread-1正在执行任务
pool-3-thread-3正在执行任务
pool-3-thread-4正在执行任务
pool-3-thread-1正在执行任务
pool-3-thread-3正在执行任务
pool-3-thread-4正在执行任务
pool-3-thread-5正在执行任务
pool-3-thread-4正在执行任务
pool-3-thread-6正在执行任务
pool-3-thread-7正在执行任务
pool-3-thread-8正在执行任务
pool-3-thread-9正在执行任务
pool-3-thread-2正在执行任务
pool-3-thread-6正在执行任务
pool-3-thread-1正在执行任务
pool-3-thread-3正在执行任务
时间:1628926041159
时间:1628926041160
时间:1628926044172--pool-5-thread-1正在执行任务
时间:1628926044172--pool-4-thread-2正在执行任务
时间:1628926044172--pool-4-thread-1正在执行任务
时间:1628926044172--pool-4-thread-2正在执行任务
时间:1628926044172--pool-5-thread-1正在执行任务
时间:1628926044172--pool-4-thread-2正在执行任务
时间:1628926044172--pool-4-thread-1正在执行任务
时间:1628926044172--pool-5-thread-1正在执行任务
时间:1628926044172--pool-5-thread-1正在执行任务
时间:1628926044172--pool-5-thread-1正在执行任务

👏 new ThreadPoolExecutor() 自定义创建


public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
        int maximumPoolSize,//线程池的最大线程数
        long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
        TimeUnit unit,//时间单位
        BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
        ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
        RejectedExecutionHandler handler) //拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

AbortPolicy:默认的拒绝策略,直接抛出异常。`throws RejectedExecutionException`。
CallerRunsPolicy:只用调用者所在线程来运行任务(提交任务的线程自己去执行该任务)。
DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。
DiscardPolicy:不处理,直接丢弃任务,没有任何异常抛出。

执行流程:

✨创建多少线程合适

一般多线程执行的任务类型可以分为 CPU 密集型 和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。创建多少线程合适,要看多线程具体的应用场景。我们的程序一般都是 CPU 计算 和 I/O 操作交叉执行 的,由于 I/O 设备 的速度相对于 CPU 来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长 ,这种场景我们一般都称为 I/O 密集型计算 和 I/O 密集型计算 相对的就是 CPU 密集型计算,CPU 密集型计算 大部分场景下都是 纯 CPU 计算。

  1. CPU 密集型任务:多线程主要目的是提成CPU利用率,保持和CPU核数一致即可。可以将线程数设置为 N(CPU 核心数)+1 ,比 CPU 核心数 多出来的一个线程是 为了防止线程偶发的缺页中断,或者其它原因导致的 任务暂停 而带来的影响。一旦 任务暂停 ,CPU 就会处于 空闲状态 ,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
  2. 测试代码如下,结果还是自己亲力亲为吧实践出真知

package com.base.demo.design.play;

import java.util.List;


public class CPUTypeTest implements Runnable {

    
    List<Long> wholeTimeList;

    
    List<Long> runTimeList;

    private long initStartTime = 0;

    
    public CPUTypeTest(List<Long> runTimeList, List<Long> wholeTimeList) {
        initStartTime = System.currentTimeMillis();
        this.runTimeList = runTimeList;
        this.wholeTimeList = wholeTimeList;
    }

    
    public boolean isPrime(final int number) {
        if (number <= 1)
            return false;

        for (int i = 2; i <= Math.sqrt(number); i++) {
            if (number % i == 0)
                return false;
        }
        return true;
    }


    
    public int countPrimes(final int lower, final int upper) {
        int total = 0;
        for (int i = lower; i <= upper; i++) {
            if (isPrime(i))
                total++;
        }
        return total;
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        countPrimes(1, 1000000);
        long end = System.currentTimeMillis();


        long wholeTime = end - initStartTime;
        long runTime = end - start;
        wholeTimeList.add(wholeTime);
        runTimeList.add(runTime);
        System.out.println("单个线程花费时间:" + (end - start));
    }

}

I/O 密集型任务:这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N(CPU 核心数)。


package com.base.demo.design.play;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;


public class IOTypeTest implements Runnable {

    
    Vector<Long> wholeTimeList;

    
    Vector<Long> runTimeList;

    private long initStartTime = 0;

    
    public IOTypeTest(Vector<Long> runTimeList, Vector<Long> wholeTimeList) {
        initStartTime = System.currentTimeMillis();
        this.runTimeList = runTimeList;
        this.wholeTimeList = wholeTimeList;
    }

    
    public void readAndWrite() throws IOException {
        File sourceFile = new File("D:/test.txt");
        //创建输入流
        BufferedReader input = new BufferedReader(new FileReader(sourceFile));
        //读取源文件,写入到新的文件
        String line = null;
        while ((line = input.readLine()) != null) {
            //System.out.println(line);
        }
        //关闭输入输出流
        input.close();
    }

    @Override
    public void run() {
        long start = System.currentTimeMillis();
        try {
            readAndWrite();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();


        long wholeTime = end - initStartTime;
        long runTime = end - start;
        wholeTimeList.add(wholeTime);
        runTimeList.add(runTime);
        System.out.println("单个线程花费时间:" + (end - start));
    }

}

喜欢折腾的同学可以试试(我贴出来的两段 TEST代码):在不同线程数的情况下,run 方法运行时间的差异。可以通过创建不同数量的线程,线程中 new 该 Test 对象(new 两个 List 传到构造参数里)并提交到线程池。查看并归纳计算得出结果。

附:线程池原理

学习线程池的实现原理,有助于你更好地理解内容。

在 HotSpot VM 的线程模型中,Java 线程被一对一映射为内核线程。Java 在使用线程执行程序时,需要创建一个内核线程;当该 Java 线程被终止时,这个内核线程也会被回收。因此 Java 线程的创建与销毁将会消耗一定的计算机资源,从而增加系统的性能开销。

除此之外,大量创建线程同样会给系统带来性能问题,因为内存和 CPU 资源都将被线程抢占,如果处理不当,就会发生内存溢出、CPU 使用率超负荷等问题。

为了解决上述两类问题,Java 提供了线程池概念,对于频繁创建线程的业务场景,线程池可以创建固定的线程数量,并且在操作系统底层,轻量级进程将会把这些线程映射到内核。

线程池可以提高线程复用,又可以固定最大线程使用量,防止无限制地创建线程。当程序提交一个任务需要一个线程时,会去线程池中查找是否有空闲的线程,若有,则直接使用线程池中的线程工作,若没有,会去判断当前已创建的线程数量是否超过最大线程数量,如未超过,则创建新线程,如已超过,则进行排队等待或者直接抛出异常。

🎉最后

在不同的业务场景以及不同配置的部署机器中,线程池的线程数量设置是不一样的。其设置不宜过大,也不宜过小,要根据具体情况,计算出一个大概的数值,再通过实际的性能测试,计算出一个合理的线程数量。

到此这篇关于Java线程池大小设置的文章就介绍到这了,更多相关Java线程池大小设置内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯