大话线程池

一.什么是线程池?

它可以在程序启动时创建一定数量的线程,并将这些线程放入一个池中。当需要执行任务时,可以从线程池中获取一个空闲的线程来执行任务,任务执行完毕后,线程会返回到线程池中等待下一次任务的分配。

二.为什么要使用线程池

  • 问题一:反复创建线程开销大
  • 问题二:过多的线程会占用太多内存
  • 解决以上两个问题的思路:
    • 用少量的线程:避免内存占用过多
    • 让这部分线程都保持工作,且可以反复执行任务避生命周期的原因的耗损

线程的生命周期包括以下几个阶段:

  1. 新建(New):当使用new操作符创建一个线程对象时,该线程处于新建状态。
  2. 运行(Runnable):当调用start()方法启动线程之后,线程进入就绪状态,等待CPU的调度执行。
  3. 阻塞(Blocked):当线程调用了sleep()、wait()、join()、park()等方法被阻塞时,线程进入阻塞状态。
  4. 等待(Waiting):当线程调用了Object的wait()方法,或者LockSupport的park()方法,进入等待状态。
  5. 超时等待(Timed Waiting):当线程调用了sleep()、join()以及带有超时参数的Object的wait()方法,或者LockSupport的parkNanos()、parkUntil()方法,进入超时等待状态。
  6. 终止(Terminated):当线程run()方法执行完毕或者调用了stop()、interrupt()等方法导致线程终止时,线程进入终止状态。
    注意:线程进入终止状态并不意味着线程被销毁,线程对象仍然存在,可以通过isAlive()方法判断线程是否存活。

三.线程池的好处

  1. 加快响应速度
  2. 合理利用cpu和内存
  3. 统一管理

四.适用的场景

  1. 服务器接受大量请求时,减少线程创建和销毁次数
  2. 开发中,创建5个以上的线程的时候。

五.构造参数

参数名 类型 说明
corePoolSize int 核心线程数
maxPoolSize int 最大线程数
keepAliveTime long 存活的时间
workQueue BlockingQueue 任务队列
threadFactory ThreadFactory 线程工厂
Handler RejectedExecutionHandler 拒绝策略

六.添加线程说明

  1. 线程数小于corePoolSize,即使其他工作线程处于空闲状态,也会创建一个新线程来运行新任务;
  2. 线程数等于(或大于)corePoolSize但小于maximumPoolSize,新提交的任务将被放入任务队列中等待执行;
  3. 队列已满,并且线程数小于maxPoolSize,则创建个新线程来运行任务;
  4. 线程数大于或者等于maxPoolSize,

maximumPoolSize是线程池中允许创建的最大线程数。当线程池中的线程数达到这个值时,新的任务将被阻塞,直到有线程可用或者等待超时。这个参数通常需要根据系统的负载情况和可用资源来进行调整,以保证系统的性能和稳定性。如果设置过小,可能会导致任务无法及时处理,影响系统的响应速度;如果设置过大,可能会浪费系统资源,导致系统负载过高。

举个例子:
线程池:corePoolSize大小为5,maxPoolSize为10,workQueue队列为100。
解释:因为线程中的请求最多会创建5个,然后任务将被添加到队列中,直到达到100。当队列已满时,将创建最新的线程maxPoolSize,最多到10个线程,如果再来任务,就拒绝。
增减线程的特点:

  1. 通过设置corePoolSize和maximumPoolSize 相同,就可以创建固定大小的线程池。

  2. 线程池希望保持较少的线程数,并且只有在负载变得很大时才增加它。

  3. 通过设置maximumPoolSize为很高的值,例如IntegerMAX VALUE,可以允许线程池容纳任意数量的并发任务A.是只有在队列填满时才创建多于corePoolSize的线程,所以如果你使用的是无界队列(例如LinkedBlockingQueue ),那么线程数就不会超过corePoolSize。

七.线程池创建

工作队列:

  • 直接交接:SynchronousQueue
  • 无界队列:LinkedBlockingQueue
  • 有界的队列: ArrayBlockingQueue

newFixedThreadPool

  • 由于传进去的LinkedBlockingQueue是没有容量上限的所以当请求数越来越多,并且无法及时处理完毕的时候也就是请求堆积的时候,会容易造成占用大量的内存,可能会导致OOM。

大话线程池_第1张图片

public class FixedThreadPoolOOM {

    private static ExecutorService executorService = Executors.newFixedThreadPool(1);
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executorService.execute(new SubThread());
        }
    }

}

class SubThread implements Runnable {


    @Override
    public void run() {
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
大话线程池_第2张图片

idea jvm参数设置参考
https://blog.csdn.net/by_talang/article/details/128199540?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170090027116800197011173%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=170090027116800197011173&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-128199540-null-null.142v96pc_search_result_base9&utm_term=idea%20%E8%AE%BE%E7%BD%AEjvm%E5%A0%86&spm=1018.2226.3001.4187

newSingleThreadExecutor

同上一样也会发生oom 因为用的无界限的队列
大话线程池_第3张图片

newCachedThreadPool

可以缓存线程,从源码看也会发生oom。因对最大线数:Integer.MAX_VALUE 线程过多导致oom
大话线程池_第4张图片

public class CachedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000000; i++) {
            executorService.execute(new Task());
        }
    }
}

大话线程池_第5张图片

newScheduledThreadPool

支持定时及周期性任务执行的线程池

public class ScheduledThreadPoolTest {

    public static void main(String[] args) {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
        threadPool.scheduleAtFixedRate(new Task(), 1, 3, TimeUnit.SECONDS);
    }
}

大话线程池_第6张图片

  • command:要执行的任务。
  • initialDelay:第一次执行任务前的延迟时间。
  • period:连续执行任务之间的时间间隔。
  • unit:initialDelay和period参数的时间单位。

八.总结

线程池里的线程数量设定为多少比较合适:

  1. CPU密集型(加密、计算hash等) : 最佳线程数为CPU核心数的1-2倍左右。
  2. 耗时IO型(读写数据库、文件、网络读写等 ) : 最佳线程数般会大于cpu核心数很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上,参考Brain Goetz推荐的计算方法:
  3. 线程数=CPU核心数*( 1+平均等待时间/平均工作时间 )

你可能感兴趣的:(多线程,java)