精华内容
下载资源
问答
  • 线程数
    千次阅读
    2022-03-09 16:42:53

    一、源码doc:

    java.util.concurrent public class ThreadPoolExecutor extends AbstractExecutorService

    一个ExecutorService ,它使用可能的多个池线程之一执行每个提交的任务,通常使用Executors工厂方法进行配置。

    线程池解决了两个不同的问题:由于减少了每个任务的调用开销,它们通常在执行大量异步任务时提供改进的性能,并且它们提供了一种限制和管理资源的方法,包括执行一组线程时消耗的线程。任务。每个ThreadPoolExecutor还维护一些基本的统计信息,例如完成任务的数量。

    为了在广泛的上下文中有用,这个类提供了许多可调整的参数和可扩展性挂钩。但是,建议程序员使用更方便的Executors工厂方法Executors.newCachedThreadPool (无界线程池,具有自动线程回收功能)、 Executors.newFixedThreadPool (固定大小的线程池)和Executors.newSingleThreadExecutor (单后台线程),这些方法预先配置了最常见的使用场景。否则,在手动配置和调整此类时使用以下指南:

    核心和最大池大小
    ThreadPoolExecutor将根据 corePoolSize (请参阅getCorePoolSize )和 maximumPoolSize (请参阅getMaximumPoolSize )设置的边界自动调整池大小(请参阅getPoolSize )。当在方法execute(Runnable)中提交了一个新任务,并且运行的线程少于 corePoolSize 时,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求。如果运行的线程数多于 corePoolSize 但少于 maximumPoolSize,则仅当队列已满时才会创建新线程。通过将 corePoolSize 和 maximumPoolSize 设置为相同,您可以创建一个固定大小的线程池。通过将 maximumPoolSize 设置为基本上无界的值,例如Integer.MAX_VALUE ,您可以允许池容纳任意数量的并发任务。最典型的是,核心和最大池大小仅在构造时设置,但它们也可以使用setCorePoolSize和setMaximumPoolSize动态更改。
    按需施工
    默认情况下,即使是核心线程也仅在新任务到达时才最初创建和启动,但这可以使用方法prestartCoreThread或prestartAllCoreThreads动态覆盖。如果您使用非空队列构造池,您可能希望预启动线程。
    创建新线程
    新线程是使用ThreadFactory创建的。如果没有另外指定,则使用 E Executors.defaultThreadFactory ,它创建的线程都在同一个ThreadGroup中,并且具有相同的NORM_PRIORITY优先级和非守护进程状态。通过提供不同的 ThreadFactory,您可以更改线程的名称、线程组、优先级、守护进程状态等。如果ThreadFactory在从newThread返回 null 时未能创建线程,则执行程序将继续,但可能无法执行任何任务。线程应该拥有“modifyThread” RuntimePermission 。如果工作线程或其他使用该池的线程不具备此权限,则服务可能会降级:配置更改可能无法及时生效,关闭池可能会一直处于可以终止但未完成的状态。
    保活次数
    如果池当前有多个 corePoolSize 线程,多余的线程如果空闲时间超过 keepAliveTime(请参阅getKeepAliveTime(TimeUnit) )将被终止。这提供了一种在池没有被积极使用时减少资源消耗的方法。如果池稍后变得更加活跃,则将构造新线程。此参数也可以使用方法setKeepAliveTime(long, TimeUnit)动态更改。使用Long.MAX_VALUE TimeUnit.NANOSECONDS的值可以有效地禁止空闲线程在关闭之前终止。默认情况下,keep-alive 策略仅适用于超过 corePoolSize 线程的情况。但是方法allowCoreThreadTimeOut(boolean)也可用于将此超时策略应用于核心线程,只要 keepAliveTime 值不为零即可。
    排队
    任何BlockingQueue都可以用来传输和保存提交的任务。此队列的使用与池大小交互:

    • 如果运行的线程少于 corePoolSize,则 Executor 总是更喜欢添加新线程而不是排队。
    • 如果 corePoolSize 或更多线程正在运行,Executor 总是更喜欢排队请求而不是添加新线程。
    • 如果请求无法排队,则会创建一个新线程,除非这将超过 maximumPoolSize,在这种情况下,该任务将被拒绝。

    排队的一般策略有以下三种:

    1.直接交接。工作队列的一个很好的默认选择是SynchronousQueue ,它将任务交给线程而不用其他方式保留它们。在这里,如果没有立即可用的线程来运行任务,则尝试将任务排队将失败,因此将构造一个新线程。在处理可能具有内部依赖关系的请求集时,此策略可避免锁定。直接切换通常需要无限的 maximumPoolSizes 以避免拒绝新提交的任务。这反过来又承认了当命令的平均到达速度快于它们的处理速度时,线程无限增长的可能性。
    2.无界队列。当所有 corePoolSize 线程都忙时,使用无界队列(例如没有预定义容量的LinkedBlockingQueue )将导致新任务在队列中等待。因此,不会创建超过 corePoolSize 个线程。 (因此,maximumPoolSize 的值没有任何影响。)当每个任务完全独立于其他任务时,这可能是合适的,因此任务不会影响彼此的执行;例如,在网页服务器中。虽然这种排队方式在平滑请求的瞬时突发方面很有用,但它承认当命令平均到达速度快于处理速度时,工作队列可能会无限增长。
    3.有界队列。有界队列(例如ArrayBlockingQueue )在与有限的 maximumPoolSizes 一起使用时有助于防止资源耗尽,但可能更难以调整和控制。队列大小和最大池大小可以相互权衡:使用大队列和小池可以最大限度地减少 CPU 使用率、操作系统资源和上下文切换开销,但可能会导致人为地降低吞吐量。如果任务经常阻塞(例如,如果它们受 I/O 限制),系统可能能够为比您允许的更多线程安排时间。使用小队列通常需要更大的池大小,这会使 CPU 更忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量。
    被拒绝的任务
    当 Executor 关闭时,以及 Executor 对最大线程和工作队列容量都使用有限的界限,并且已经饱和时,在方法execute(Runnable)中提交的新任务将被拒绝。无论哪种情况, execute方法都会调用其RejectedExecutionHandler的RejectedExecutionHandler.rejectedExecution(Runnable,ThreadPoolExecutor)方法。提供了四个预定义的处理程序策略:
    1.在默认的ThreadPoolExecutor.AbortPolicy中,处理程序在拒绝时抛出运行时RejectedExecutionException 。
    2.在ThreadPoolExecutor.CallerRunsPolicy中,调用execute本身的线程运行任务。这提供了一种简单的反馈控制机制,可以减慢提交新任务的速度。
    3.在ThreadPoolExecutor.DiscardPolicy中,无法执行的任务被简单地丢弃。
    4.在ThreadPoolExecutor.DiscardOldestPolicy中,如果 executor 没有关闭,则丢弃工作队列头部的任务,然后重试执行(可能再次失败,导致重复此操作。)

    可以定义和使用其他类型的RejectedExecutionHandler类。这样做需要小心谨慎,尤其是当策略设计为仅在特定容量或排队策略下工作时。
    挂钩方法
    此类提供protected的可beforeExecute(Thread, Runnable)和afterExecute(Runnable, Throwable)方法,这些方法在执行每个任务之前和之后调用。这些可用于操纵执行环境;例如,重新初始化 ThreadLocals、收集统计信息或添加日志条目。此外,可以重写方法terminated以执行任何特殊处理,一旦 Executor 完全终止需要完成。
    如果钩子或回调方法抛出异常,内部工作线程可能会依次失败并突然终止。
    队列维护
    方法getQueue()允许访问工作队列以进行监视和调试。强烈建议不要将此方法用于任何其他目的。当大量排队的任务被取消时,提供的两个方法remove(Runnable)和purge可用于协助存储回收。
    定稿
    程序中不再引用且没有剩余线程的池将自动shutdown 。如果您想确保即使用户忘记调用shutdown也能回收未引用的池,那么您必须通过设置适当的保持活动时间、使用零核心线程的下限和/或设置allowCoreThreadTimeOut(boolean)来安排未使用的线程最终死亡allowCoreThreadTimeOut(boolean) 。
    扩展示例。此类的大多数扩展都会覆盖一个或多个受保护的挂钩方法。例如,这是一个添加简单暂停/恢复功能的子类:

    class PausableThreadPoolExecutor extends ThreadPoolExecutor {
       private boolean isPaused;
       private ReentrantLock pauseLock = new ReentrantLock();
       private Condition unpaused = pauseLock.newCondition();
    
       public PausableThreadPoolExecutor(...) { super(...); }
    
       protected void beforeExecute(Thread t, Runnable r) {
         super.beforeExecute(t, r);
         pauseLock.lock();
         try {
           while (isPaused) unpaused.await();
         } catch (InterruptedException ie) {
           t.interrupt();
         } finally {
           pauseLock.unlock();
         }
       }
    
       public void pause() {
         pauseLock.lock();
         try {
           isPaused = true;
         } finally {
           pauseLock.unlock();
         }
       }
    
       public void resume() {
         pauseLock.lock();
         try {
           isPaused = false;
           unpaused.signalAll();
         } finally {
           pauseLock.unlock();
         }
       }
     }
    

    自从:1.5

    二、个人总结:

    1、核心线程数配置个数:看IO密集型,还是CPU密集型
    (1)一般平时cpu使用率4%以下,都是IO密集型,IO密集型核心线程数设置大小具体看实践,目前项目里核心线程数设置50,最大线程数可以和核心线程数相同,队列配置大一些,使永远触发不到最大线程数
    (2)如果是大量计算CPU使用率过高,属于CPU密集型,CPU密集型以4C8G为例,核心线程数一般设置4,最大线程数可以和核心线程数相同,队列配置大一些,使永远触发不到最大线程数

    2、核心线程数销毁
    (1)默认情况下,keep-alive 策略仅适用于超过 corePoolSize 线程的情况,没有任务会进行空跑, 和线程池生命周期一样, 除非线程池shutdown;但是方法allowCoreThreadTimeOut(boolean)也可用于将此超时策略应用于核心线程,只要 keepAliveTime 值不为零即可

    更多相关内容
  • 线程池的初始参数中包含 核心线程数、最大线程数、线程池线程空闲回收时间、阻塞队列、线程工厂、拒绝策略。 那么线程池是如何管理线程和阻塞队列的关系呢? 验证思路 确保先提交的任务优先被线程池处理 多线程执行...

    线程池内部运行过程

    线程池的初始参数中包含 核心线程数、最大线程数、线程池线程空闲回收时间、阻塞队列、线程工厂、拒绝策略。
    那么线程池是如何管理线程和阻塞队列的关系呢?

    验证思路

    确保先提交的任务优先被线程池处理

    多线程执行本身是无序的,原因在于并发操作下,后面提交的任务也可能被先运行,因此,为了确保先提交的任务优先被线程池处理,我们使用延迟提交下一个任务的方法,确保已提交的任务优先获得线程池处理的机会(处理不等于执行)

    确保执行的任务占有足够久的线程池资源

    为了方便观察核心线程数、阻塞队列和最大线程数之间的关系,我们需要确保一个任务在执行的时候占用足够久的时间,以确保下一个任务到来之时,之前的任务还占用着线程池的资源,因而可以进入到阻塞队列或者新申请一个线程池的线程资源

    可观测的阻塞队列大小

    我们使用 ArrayBlockingQueue 指定阻塞队列大小,用以验证阻塞队列和最大线程数之间的关系

    充分的信息

    自定义一个线程池,然后先运行6个线程任务,然后等待所有任务执行完,然后再次提交6个线程任务,然后等待所有任务执行完,期间不断打印线程池的活跃线程数、线程阻塞队列大小、完成任务、线程池线程和执行任务之间的关系

    代码

    自定义线程池

    import lombok.SneakyThrows;
    
    import java.util.concurrent.*;
    
    /**
     * @Title: 线程池运行原理
     * @Description:
     * @Author: bestcxx
     * @Version: v1.0
     * @Date:2021-12-05
     * @Updatedby:
     */
    public class ThreadPool {
        private final static ThreadPoolExecutor executor = getThreadPool();
    
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 31; i++) {
                printPoolInfo(executor);
                if (i < 6 || (i >= 15 && i <= 20)) {
                    getThreadPool().execute(new MyThread("任务" + i));
                    //getThreadPool().submit(new MyThread("任务" + i));
                }
                Thread.sleep(900);
            }
    
            //ArrayBlockingQueue;
            //LinkedBlockingQueue;
            //PriorityBlockingQueue;
            //DelayQueue;
            //SynchronousQueue;
            //LinkedBlockingDeque;
            //LinkedTransferQueue;
    /**
         CAPACITY   00011111111111111111111111111111    536870911
         ~CAPACITY  11100000000000000000000000000000    -536870912
         RUNNING    11100000000000000000000000000000    -536870912
         SHUTDOWN   00000000000000000000000000000000    0
         STOP       00100000000000000000000000000000    536870912
         TIDYING    01000000000000000000000000000000    1073741824
         TERMINATED 01100000000000000000000000000000    1610612736
        */
    
        }
    
        public static class MyThread extends Thread {
            String threadName = 0 + "";
    
            public MyThread(String threadName) {
                this.threadName = threadName;
            }
    
            @SneakyThrows
            @Override
            public void run() {
                super.run();
                System.out.println("线程资源:" + Thread.currentThread().getName() + " " + threadName);
                Thread.sleep(6000L);
            }
        }
    
        private static class ThreadPoolExecutorHolder {
            private static ThreadPoolExecutor instance = new ThreadPoolExecutor(2, 4, 0,
                    TimeUnit.SECONDS, new ArrayBlockingQueue<>(2));
        }
    
        public static synchronized ThreadPoolExecutor getThreadPool() {
            return ThreadPoolExecutorHolder.instance;
        }
    
        public static void printPoolInfo(ThreadPoolExecutor executor) {
            System.out.println(String.format(
                    "设定核心线程数: %d," +
                            "设定最大线程数: %d," +
                            "活跃线程数: %d ," +
                            "阻塞队列任务数: %d," +
                            "总任务数: %d," +
                            "已执行完任务数: %d",
                    executor.getCorePoolSize(), executor.getMaximumPoolSize(), executor.getActiveCount(),
                    executor.getQueue().size(), executor.getTaskCount(), executor.getCompletedTaskCount()));
        }
    
    }
    
    

    结果

    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 0 ,阻塞队列任务数: 0,总任务数: 0,已执行完任务数: 0
    线程资源:pool-1-thread-1 任务0
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 1 ,阻塞队列任务数: 0,总任务数: 1,已执行完任务数: 0
    线程资源:pool-1-thread-2 任务1
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 2,已执行完任务数: 0
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 1,总任务数: 3,已执行完任务数: 0
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 2,总任务数: 4,已执行完任务数: 0
    线程资源:pool-1-thread-3 任务4
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 3 ,阻塞队列任务数: 2,总任务数: 5,已执行完任务数: 0
    线程资源:pool-1-thread-4 任务5
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 2,总任务数: 6,已执行完任务数: 0
    线程资源:pool-1-thread-1 任务2
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 1,总任务数: 6,已执行完任务数: 1
    线程资源:pool-1-thread-2 任务3
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 2
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 2
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 2
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 3 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 3
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 4
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 4
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 1 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 5
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 0 ,阻塞队列任务数: 0,总任务数: 6,已执行完任务数: 6
    线程资源:pool-1-thread-1 任务15
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 1 ,阻塞队列任务数: 0,总任务数: 7,已执行完任务数: 6
    线程资源:pool-1-thread-2 任务16
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 8,已执行完任务数: 6
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 1,总任务数: 9,已执行完任务数: 6
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 2,总任务数: 10,已执行完任务数: 6
    线程资源:pool-1-thread-5 任务19
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 3 ,阻塞队列任务数: 2,总任务数: 11,已执行完任务数: 6
    线程资源:pool-1-thread-6 任务20
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 2,总任务数: 12,已执行完任务数: 6
    线程资源:pool-1-thread-1 任务17
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 1,总任务数: 12,已执行完任务数: 7
    线程资源:pool-1-thread-2 任务18
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 8
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 8
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 4 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 8
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 3 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 9
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 10
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 2 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 10
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 1 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 11
    设定核心线程数: 2,设定最大线程数: 4,活跃线程数: 0 ,阻塞队列任务数: 0,总任务数: 12,已执行完任务数: 12
    
    

    结论

    基本几结论

    • 线程池初始化的活跃线程数为0
    • 当活跃线程数<核心线程数,且活跃线程在执行任务,线程池会新生成线程用以执行提交的任务
    • 当活跃线程数=核心线程数,且活跃线程在执行任务,新任务会优先被放置到阻塞队列
    • 当活跃线程数=核心线程数,且活跃线程在执行任务,且阻塞队列已满,且没有活跃线程数<最大线程数,则线程池会新生成一个线程来执行任务
    • 当线程空闲,会被逐步回收,如果持续没有新任务提交,线程池活跃线程数会降低为0

    新任务提交在线程池内部处理的优先级

    核心线程 > 阻塞队列 > 扩容的线程

    展开全文
  • 多线程线程数设置多少合适

    万次阅读 多人点赞 2020-06-30 01:15:04
    线程数的设置的最主要的目的是为了充分并合理地使用 CPU 和内存等资源,从而最大限度地提高程序的性能,因此让我们一起去探索吧! 首先要考虑到 CPU 核心数,那么在 Java 中如何获取核心线程数? 可以使用 Runtime....

    前沿

    大家都用过线程池,但是线程池数量设置为多少比较合理呢?

    线程数的设置的最主要的目的是为了充分并合理地使用 CPU 和内存等资源,从而最大限度地提高程序的性能,因此让我们一起去探索吧!

    首先要考虑到 CPU 核心数,那么在 Java 中如何获取核心线程数?

    可以使用 Runtime.getRuntime().availableProcessor() 方法来获取(可能不准确,作为参考)
    或者直接去服务器查看

    温故为什么使用线程

    场景

    如果有两个任务需要处理,一个任务A,一个任务B

    方案一:一个线程执行任务A和B,A执行完后,执行B
    方案二:两个线程A和B去执行任务A 和 B,同时进行

    哪个方案会快点?应该很多人会回答,肯定是方案二啊,多线程并行去处理任务A和B,肯定快啊。是这样吗?回答这个问题之前,先带着大家去回顾梳理一下。

    线程执行

    线程的执行,是由CPU进行调度的,一个CPU在同一时刻只会执行一个线程,我们看上去的线程A 和 线程B并发执行。

    为了让用户感觉这些任务正在同时进行,操作系统利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。

    上下文切换过程是需要时间的;现在我们来看一下上面的问题,小伙伴们再看一下是哪个方案快呢?是不是有些小伙伴们会说方案一,因为不需要线程切换;方案二需要来回切换这两个线程,耗时会多点。

    小伙伴们心中此时是不是会有疑惑,那为什么会有多线程?先不急,再往下看。

    为什么要使用多线程

    小伙伴想想在我们真实业务中,我们是什么流程?
    在这里插入图片描述

    上图的流程:

    1、先发起网络请求

    2、Web服务器解析请求

    3、请求后端的数据库获取数据

    4、获取数据后,进行处理

    5、把处理结果放回给用户

    这个是我们处理业务的时候,常规的请求流程;我们看一下整个过程涉及到什么计算机处理。

    1、网络请求----->网络IO

    2、解析请求----->CPU

    3、请求数据库----->网络IO

    4、MySQL查询数据----->磁盘IO

    5、MySQL返回数据----->网络IO

    6、数据处理----->CPU

    7、返回数据给用户----->网络IO

    讲到这里,小伙伴们是不是感觉又不乱了,在真实业务中我们不单单会涉及CPU计算,还有网络IO和磁盘IO处理,这些处理是非常耗时的。如果一个线程整个流程是上图的流程,真正涉及到CPU的只有2个节点,其他的节点都是IO处理,那么线程在做IO处理的时候,CPU就空闲出来了,CPU的利用率就不高。

    小伙伴们现在知道多线程的用处了吧,对,就是为了提升CPU利用率。

    提升QPS/TPS

    衡量系统性能如何,主要指标系统的(QPS/TPS)

    QPS/TPS:每秒能够处理请求/事务的数量

    并发数:系统同时处理的请求/事务的数量

    响应时间:就是平均处理一个请求/事务需要时长

    QPS/TPS = 并发数/响应时间

    上面公式代表并发数越大,QPS就越大;所以很多人就会以为调大线程池,并发数就会大,也会提升QPS,所以才会出现一开始前言所说的,大多数人的误区。

    其实QPS还跟响应时间成反比,响应时间越大,QPS就会越小。

    虽然并发数调大了,就会提升QPS,但线程数也会影响响应时间,因为上面我们也提到了上下文切换的问题,那怎么设置线程数的呢?

    如何设置线程数

    那我们如何分配线程?我们提供一个公式:

    最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

    备注:这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。

    我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。

    从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升CPU利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。

    基础常规标准

    在确认了核心数后,再去判断是 CPU 密集型任务还是 IO 密集型任务:
    CPU 密集型任务:
    比如像加解密,压缩、计算等一系列需要大量耗费 CPU 资源的任务,大部分场景下都是纯 CPU 计算。
    IO 密集型任务:
    比如像 MySQL 数据库、文件的读写、网络通信等任务,这类任务不会特别消耗 CPU 资源,但是 IO 操作比较耗时,会占用比较多时间。

    1、CPU密集型:操作内存处理的业务,一般线程数设置为:CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 8

    2、IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40
    在知道如何判断任务的类别后,让我们分两个场景进行讨论:

    CPU 密集型任务

    对于 CPU 密集型计算,多线程本质上是提升多核 CPU 的利用率,所以对于一个 8 核的 CPU,每个核一个线程,理论上创建 8 个线程就可以了。

    如果设置过多的线程数,实际上并不会起到很好的效果。此时假设我们设置的线程数量是 CPU 核心数的 2 倍,因为计算任务非常重,会占用大量的 CPU 资源,所以这时 CPU 的每个核心工作基本都是满负荷的,而我们又设置了过多的线程,每个线程都想去利用 CPU 资源来执行自己的任务,这就会造成不必要的上下文切换,此时线程数的增多并没有让性能提升,反而由于线程数量过多会导致性能下降。

    因此,对于 CPU 密集型的计算场景,理论上线程的数量 = CPU 核数就是最合适的,不过通常把线程的数量设置为CPU 核数 +1,会实现最优的利用率。即使当密集型的线程由于偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费,从而保证 CPU 的利用率。

    如下图就是在一个 8 核 CPU 的电脑上,通过修改线程数来测试对 CPU 密集型任务(素数计算)的性能影响。
    在这里插入图片描述

    可以看到线程数小于 8 时,性能是很差的,在线程数多于处理器核心数对性能的提升也很小,因此可以验证公式还是具有一定适用性的。

    除此之外,我们最好还要同时考虑在同一台机器上还有哪些其他会占用过多 CPU 资源的程序在运行,然后对资源使用做整体的平衡。

    IO 密集型任务

    对于 IO 密集型任务最大线程数一般会大于 CPU 核心数很多倍,因为 IO 读写速度相比于 CPU 的速度而言是比较慢的,如果我们设置过少的线程数,就可能导致 CPU 资源的浪费。而如果我们设置更多的线程数,那么当一部分线程正在等待 IO 的时候,它们此时并不需要 CPU 来计算,那么另外的线程便可以利用 CPU 去执行其他的任务,互不影响,这样的话在任务队列中等待的任务就会减少,可以更好地利用资源。

    对于 IO 密集型计算场景,最佳的线程数是与程序中 CPU 计算和 IO 操作的耗时比相关的,《Java并发编程实战》的作者 Brain Goetz 推荐的计算方法如下:

    线程数 = CPU 核心数 * (1 + IO 耗时/ CPU 耗时)

    通过这个公式,我们可以计算出一个合理的线程数量,如果任务的平均等待时间长,线程数就随之增加,而如果平均工作时间长,也就是对于我们上面的 CPU 密集型任务,线程数就随之减少。可以采用 APM 工具统计到每个方法的耗时,便于计算 IO 耗时和 CPU 耗时。

    在这里引用Java并发编程实战中的图,方便大家更容易理解:
    在这里插入图片描述

    还有一派的计算方式是《Java虚拟机并发编程》中提出的:

    线程数 = CPU 核心数 / (1 - 阻塞系数)

    其中计算密集型阻塞系数为 0,IO 密集型阻塞系数接近 1,一般认为在 0.8 ~ 0.9 之间。比如 8 核 CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 个线程数
    在这里插入图片描述

    上图是 IO 密集型任务的一个测试,是在双核处理器上开不同的线程数(从 1 到 40)来测试对程序性能的影响,可以看到线程池数量达到 20 之后,曲线逐渐水平,说明开再多的线程对程序的性能提升也毫无帮助。

    太少的线程数会使得程序整体性能降低,而过多的线程也会消耗内存等其他资源,所以如果想要更准确的话,可以进行压测,监控 JVM 的线程情况以及 CPU 的负载情况,根据实际情况衡量应该创建的线程数,合理并充分利用资源。

    同时,有很多线程池的应用,比如 Tomcat、Redis、Jdbc 等,每个应用设置的线程数也是不同的,比如 Tomcat 为流量入口,那么线程数的设置可能就要比其他应用要大。

    总结

    通过对线程数设置的探究,我们可以得知线程数的设置首先和 CPU 核心数有莫大关联,除此之外,我们需要根据任务类型的不同选择对应的策略,具体的怎么设置要根据业务上需要/服务器的环境/QPS/TPS等指标等等有关系。
    线程的平均工作时间所占比例越高,就需要越少的线程;线程的平均等待时间所占比例越高,就需要越多的线程;增强cpu的使用率。
    针对不同的程序,进行对应的实际测试就可以得到最合适的选择。

    展开全文
  • Java多任务多线程,总线程数countDownLatch限制模板(附源码)

    Java多任务多线程,总线程数countDownLatch限制模板(附源码)

    问题背景

    最近开发的项目需要多任务并行运行,然后每个任务需要多线程运行,要求如下:

    • 多任务并行,一个任务可设置线程数
    • 限制整个项目开启任务的线程数500,大于500则等待线程执行完毕,再进行创建线程
    • 大于500是等待线程结束,最多等待10分钟

    注意事项:

    项目创建

    1 引入pom依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.7</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.yg</groupId>
        <artifactId>taskFrame</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>taskFrame</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    2 启动类

    package com.yg.taskframe;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class TaskFrameApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TaskFrameApplication.class, args);
        }
    
    }
    

    3 Runnable线程类

    package com.yg.taskframe.core;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.CountDownLatch;
    
    @Slf4j
    public class CreateRunnable implements Runnable {
        private CountDownLatch countDownLatch;
    
        public CreateRunnable(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.error("CreateRunnable error: ", e);
            }
            countDownLatch.countDown();
        }
    }
    

    4 线程池管理服务类

    package com.yg.taskframe.core;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * @Author suolong
     * @Date 2022/3/8 14:46
     * @Version 1.5
     */
    @Slf4j
    public class AsynThreadService {
    
        public ThreadPoolTaskExecutor asyncServiceExecutor;
        public CountDownLatch countDownLatch;
    
        public AsynThreadService(ThreadPoolTaskExecutor asyncServiceExecutor, CountDownLatch countDownLatch) {
            this.asyncServiceExecutor = asyncServiceExecutor;
            this.countDownLatch = countDownLatch;
        }
    
    
    //    public void submit(ComputeDTO computeDTO, String pathCode, List<QueryRequest.ParamItem> param) {
    //        asyncServiceExecutor.submit(new CreateRunnable(countDownLatch));
    //    }
    
        public void waitComplete() {
            try {
                //等待当前线程池对象的所有countDownLatch都已经释放,说明线程都执行完毕了,然后关闭线程池
                log.info("countDownLatch remain {}", this.countDownLatch.getCount());
                this.countDownLatch.await();
                log.info("Close asynThreadService");
                asyncServiceExecutor.shutdown();
            } catch (Exception e) {
                log.error("AsyncServiceExecutor shutdown error: ", e);
            }
        }
    }
    

    5 线程池和 countDownLatch 创建类

    package com.yg.taskframe.core;
    
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @Author suolong
     * @Date 2022/3/8 14:43
     * @Version 2.0
     */
    @Slf4j
    public class AsynThreadPool {
        private static AtomicInteger curCount = new AtomicInteger(0);
        private static Integer maxCount = 500;
    
        public static AsynThreadService createCountDownLatch(Long taskId, int threads) throws InterruptedException {
            CountDownLatch = new CountDownLatch(threads);
    
            //创建新的线程池
            ThreadPoolTaskExecutor asyncServiceExecutor = creatThreadPool(taskId, threads);
    
            //超过了CountDownLatch总数,每次等待1一分钟,总共等待十次10分钟
            if (curCount.get() >= maxCount) {
                int n = 10;
                while (n-- > 0) {
                    if (curCount.get() >= maxCount) {
                        log.info("CountDownLatch count more than 500, loading over for other threads");
                        //每一分钟检查一次,大于总的线程数,进行一分钟等待,然后继续查询是否大于总的线程数,一共等待10次
                        TimeUnit.SECONDS.sleep(60);
                    } else {
                        //一旦发现 curCount.get() < maxCount,立马结束循环
                        n = 0;
                    }
                    if (n == 0) {
                        log.warn("Part of asynThreadService aren't finish");
                    }
                }
            }
            //原子获取累加
            curCount.addAndGet(threads);
    
            AsynThreadService asynThreadService = new AsynThreadService(asyncServiceExecutor, countDownLatch);
            return asynThreadService;
        }
    
        //释放countDownLatch
        public static void free(int count) {
            log.info("CountDownLatch : {}, count: {}", curCount.get(), count);
            curCount.addAndGet(-count);
            log.info("CountDownLatch remain: {}", curCount.get());
        }
    
    
        //创建线程池
        public static ThreadPoolTaskExecutor creatThreadPool(Long taskId, int threads) {
            ThreadPoolTaskExecutor asyncServiceExecutor = null;
            try {
                asyncServiceExecutor = new ThreadPoolTaskExecutor();
                // 线程池维护线程的最少数量
                // asyncServiceExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors() + 1);
                asyncServiceExecutor.setCorePoolSize(threads);
                // 线程池维护线程的最大数量
                asyncServiceExecutor.setMaxPoolSize(threads + 1);
                // 线程池所使用的缓冲队列
                asyncServiceExecutor.setQueueCapacity(2000);
                //   asyncServiceExecutor.prefersShortLivedTasks();
                asyncServiceExecutor.setThreadNamePrefix("TaskId" + taskId.toString() + "-Thread-");
                asyncServiceExecutor.setBeanName("TaskId" + taskId);
                //  asyncServiceExecutor.setKeepAliveSeconds(20);
                //调用者执行
                //   asyncServiceExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
                asyncServiceExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
                // 线程全部结束才关闭线程池
                asyncServiceExecutor.setWaitForTasksToCompleteOnShutdown(true);
                // 如果超过60s还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
                asyncServiceExecutor.setAwaitTerminationSeconds(30);
                asyncServiceExecutor.initialize();
            } catch (Exception e) {
                log.error("Create ThreadPoolTaskExecutor failed", e);
            }
            return asyncServiceExecutor;
        }
    }
    

    6 任务创建类

    package com.yg.taskframe.core;
    
    import lombok.AllArgsConstructor;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * @Author suolong
     * @Date 2022/3/8 14:52
     * @Version 2.0
     */
    @Slf4j
    @AllArgsConstructor
    @NoArgsConstructor
    public class ParallelTask {
        private int threads;
        private String url;
    
        public ParallelTask nextTask;
        public ParallelTask prevTask;
    
        public ParallelTask(int threads) {
            this.threads = threads;
        }
    
        public AsynThreadService startTask(Long taskId) throws InterruptedException {
    
            //任务里面线程大于1,就创建线程池,创建线程池要先判断所有线程池的总数是否大于500,
            AsynThreadService asynThreadService = AsynThreadPool.createCountDownLatch(taskId, threads);
            return asynThreadService;
    
        }
    }
    

    7 启动一个任务入口类

    package com.yg.taskframe.service;
    
    import com.yg.taskframe.core.AsynThreadPool;
    import com.yg.taskframe.core.AsynThreadService;
    import com.yg.taskframe.core.ParallelTask;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.Future;
    
    /**
     * @Author suolong
     * @Date 2022/5/6 14:36
     * @Version 2.0
     */
    
    @Slf4j
    @Service
    public class TaskService {
    
    
        /**
         * @Caption 每开启一个任务就可以创建一个线程池,这就是多任务并行执行,使用countDownLatch可以限制总共的线程数,超过设置的countDownLatch,可以等待前面的任务执行完成
         * @Param
         * @Return
         */
        public void startTask(Long taskId, int threads) throws InterruptedException {
    
            log.info("Begin task");
    
            //可以通过配置设置线程数
           // int threads = threads;
            if (threads == 0) {
                threads = 1;
            }
            log.info("threads: {}", threads);
    //        Random random = new Random();
    //        //任务id可以通过配置下发
    //        Long taskId = random.nextInt(100) + 500L;
            //初始化一个任务的线程数
            ParallelTask parallelTask = new ParallelTask(threads);
            //创建单个任务线程为threads的线程池,并设置任务名为taskId组合
            AsynThreadService asynThreadService = parallelTask.startTask(taskId);
            //单线程查询doquery
            List<String> params = new ArrayList<>();
            params.add("1");
            submit(params, asynThreadService);
    
            //线程执行完成,关闭线程池,减去countDownLatch
            int countDownLatchCount = (int) asynThreadService.countDownLatch.getCount();
            while (countDownLatchCount-- > 0) {
                asynThreadService.countDownLatch.countDown();
            }
            asynThreadService.waitComplete();
            AsynThreadPool.free(threads);
        }
    
    
        public void submit(List<String> params, AsynThreadService asynThreadService) {
    
            //执行线程
            log.info("Begin 0004 thread");
            Future<String> futureResult = asynThreadService.asyncServiceExecutor.submit(() ->
                    submitSingle(params, asynThreadService.countDownLatch)
            );
    
        }
    
    
        /**
         * @Caption
         * @Param
         * @Return 异步线程返回结果
         */
        public String submitSingle(List<String> params, CountDownLatch countDownLatch) {
            log.info("Submit single thread");
            try {
                log.info("params size: {}", params.size());
                long start = System.currentTimeMillis();
                //可以远程调用其他服务
                String resultVOlist = doQuery(params);
                log.info("resultVOlist:{}", resultVOlist);
                return resultVOlist;
            } catch (Exception e) {
                log.error("dataxQueryController not found error", e);
                return "dataxQueryController not found error";
            } finally {
                log.info("Close one countDownLatch");
                countDownLatch.countDown();
            }
    
        }
    
    
        public String doQuery(List<String> params) {
            if (params.size() < 10) {
                return "success";
            }
            return "fail";
        }
    
    }
    

    8 使用接口添加任务,通过需要设置每个线程池的线程数和任务ID

    package com.yg.taskframe.controller;
    
    import com.yg.taskframe.service.TaskService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @Author suolong
     * @Date 2022/5/6 15:07
     * @Version 2.0
     */
    
    @Slf4j
    @RestController
    public class TaskController {
    
        @Autowired
        TaskService taskService;
    
    
        @PostMapping("/task")
        public String addTask(@RequestParam Long taskId, @RequestParam int threads) {
            try {
                taskService.startTask(taskId, threads);
                return "success";
            } catch (Exception e) {
                log.error("Exception", e);
                return "fail";
            }
        }
    }
    

    9 整个项目目录

    总结

    • 通过模板可以进行多任务并行运行,并且可以控制总线程数




    作为程序员第 122 篇文章,每次写一句歌词记录一下,看看人生有几首歌的时间,wahahaha …

    Lyric: 在月光下一直找寻

    展开全文
  • 线程池核心数与最大线程数设置

    千次阅读 2022-07-01 11:05:52
    线程池核心数与最大线程数设置
  • ThreadPoolExecutor是JDK中的线程池实现,这个类实现了一个线程池需要的各个方法,它实现了任务提交、线程管理、监控等等方法。 来看看ThreadPoolExecutor类的构造方法源码,其他创建线程池的方法最终都会导向这个...
  • 【杂谈】最优线程数到底如何确定?

    千次阅读 多人点赞 2022-02-27 21:36:24
    当然线程数也不是越多越好,因为上下文切换也是一个耗时操作,于是我们就希望给我们的系统找到一个最优的线程数,这样既能保证充分利用CPU资源,又能减少上下文切换所带来的的开销 《Java Concurrency in Practice...
  • Java线程池的核心线程数和最大线程数

    万次阅读 多人点赞 2021-03-14 19:24:35
    //队列最大容量 //当提交的任务个大于QueueCapacity,就需要设置该参数,但spring提供的都不太满足业务场景,可以自定义一个,也可以注意不要超过QueueCapacity即可 taskExecutor.setRejectedExecutionHandler(new...
  • 瓶口:最大线程数 瓶颈:队列 瓶身容量:核心线程数 线程池SingleThreadPool 和线程池FixedThreadPool wesi
  • Tomcat配置访问日志和线程数

    千次阅读 2022-03-22 22:54:22
    配置Tomcat的访问日志和核心线程数
  • linux查看cpu个数,线程数及cpu型号

    千次阅读 2021-05-14 11:49:01
    1.查看CPU逻辑idgrep 'physical id' /proc/cpuinfo | sort -uphysical...查看物理CPU个$ cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l23.查看每个物理CPU中core的个数$ cat /proc/cpuinfo| grep "cp...
  • 误区一:jmeter 线程数就是用户并发数 业务需求:要求tps达到500/s,1000并发数2秒完成处理 大部分的测试同学是这样压测的,将jmeter的线程数直接从200、300、400这样调试到tps出现拐点。如果400线程符合了,就说明...
  • Java线程池核心线程数与最大线程数的区别

    万次阅读 多人点赞 2020-06-23 15:20:45
    corePoolSize:核心线程数;maximunPoolSize:最大线程数 每当有新的任务到线程池时, 第一步: 先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,且任务结束后将该线程保留在...
  • tomcat设置线程数

    千次阅读 2022-04-10 16:03:07
    查看Tomcat线程数 1、Tomcat默认线程数200 2、修改server.xml文件,增加maxThreads、minSpareThreads、maxSpareThreads、acceptCount 3、参数解释 maxThreads=“1000” 最大并发数 minSpareThreads=“100”///初始...
  • java线程池合理设置最大线程数和核心线程数

    万次阅读 多人点赞 2020-10-10 17:13:31
    这个时候就需要使用多线程去处理。 一开始是这么配置的: @Configuration @EnableAsync(proxyTargetClass = true)//利用@EnableAsync注解开启异步任务支持 @ComponentScan({"com.ctfojt.auditbcarslogo.service"}) /...
  • 首先看看springboot内置的tomcat,该如何配置这两个参数# 在配置文件中添加如下内容# tomcat最大线程数,默认为200server.tomcat.max-threads=200# tomcat最大连接数,默认为10000(网上的说法)server.tomcat.max-...
  • 线程池核心线程数的设置

    千次阅读 2021-07-17 11:16:37
    能搜到这个问题的必然是知道自己为什么来的,线程池作为池化思想的线程实现,可以为我们减少创建和销毁线程所带来的功耗,具体设置...如果是CPU密集型,则设置线程数为N+1(某时因为发生一个页面错误或其他原因而暂停
  • 实际上客户端发起的线程数与服务器可达到的并发数并无直接关系,但你应该使用足够的线程数,让服务器达到事务饱和。 如何判断服务器是否达到饱和?这时我们可以采取阶梯增压 的方式,不断加大客户端线程数量,直到...
  • linux 查看线程数的方法

    千次阅读 2022-05-31 19:10:50
    其中Threads后面跟的就是线程数。 或者:通过 ls /proc/{pid}/task | wc -l 二、使用top命令 具体用法是 top -H 加上这个选项,top的每一行就不是显示一个进程,而是一个线程。 top -H top -H -p {pi.
  • 前提说明 为了确保服务不会被过多的http长连接压垮,我们需要对tomcat设定个最大连接,超过这个...达到保护自己的同时起到连接负载均衡的作用。 动手去做 一开始根据故障todoList提供的参数MaxKeepAliveReques...
  • 在高并发的情况下采用线程池,可以有效降低线程创建释放的时间花销及资源开销,如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及“过度切换”(在JVM中采用的处理机制为时间片轮转,减少了线程...
  • Linux查看进程所占的线程数

    千次阅读 2021-12-02 15:25:18
    1、先top查看pid 2、进程包含的线程数 pstree -p 23327 |wc -l
  • 1.CPU个数2.每个CPU的核心数3.线程数
  • 摘自: Java线程池的核心线程数和最大线程数总是容易混淆怎么办
  • CPU的核心数与线程数

    千次阅读 2021-03-19 21:02:37
    1、线程数可以模拟出不同的CPU核心数。 CPU的核数是指硬件上有多个核,线程数可以模拟多个核的功能。线程越多,就越有利于同时运行多个程序,因为线程数等于CPU在某一时刻可以同时并行处理的任务数。 2、对于一个...
  • 只要是有数据读写的应用都有一个最大连接数和最大线程的配置,配置合理的最大连接数和线程数,可以有效防止应用崩溃 一. tomcat: tomcat是目前较为常用的Web容器,良好的配置能使用tomcat服务效率更高, 今天我...
  • CPU核心数与线程数详解

    万次阅读 多人点赞 2020-03-29 21:58:42
    CPU就是中央处理单元,物理CPU就是CPU的硬件个(socket)。 核心: 一开始,每个物理 cpu 上只有一个核心(a single core),对操作系统而言,也就是同一时刻只能运行一个进程/线程。 为了提高性能,cpu ...
  • springboot tomcat 线程数相关配置

    千次阅读 2022-03-22 22:36:56
    server.tomcat.accept-count =100 # 当所有...server.tomcat.max-threads =200 #最大工作线程数. server.tomcat.min-spare-threads =10 # 最小工作线程数. 理解 请求进来后, 会创建线程, 数量达到 max-threads, 此时
  • 如何合理设置线程池的核心线程数? 当线程池的核心线程数量过大或者过小有没影响?如何合理地设置线程池的核心线程的数量?这个是在日常开发中程序员在使用线程池时经常需要考虑的问题,下面具体介绍下。 1、当...
  • 一 cpu个数、核数、线程数的关系 cpu个数:是指物理上,也及硬件...线程数:是同一时刻设备能并行执行的程序个数,线程数=cpu个数 * 核数 二 cpu线程数和Java多线程 首先明白几个概念: (1) 单个cpu线程在同...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,176,073
精华内容 470,429
关键字:

线程数