java并发-线程池

java并发-线程池

线程池的介绍

Java线程池表示一组等待工作并多次重用的工作线程。在线程池的情况下,创建了一组固定大小的线程。服务提供者从线程池中拉出一个线程并分配一个作业。完成作业后,线程再次包含在线程池中。

使用线程池可以节省多线程应用程序中的资源,同时加快响应速度。线程池还可以指定线程的数量,避免线程过多

对每个任务都开一个线程的情况:

public class OneThread {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new Task());
            thread.start();
        }
        
    }

    static class Task implements Runnable {

        @Override
        public void run() {
            System.out.println("finished");
        }
    }
}

/*
* finished
finished
finished
finished
finished
finished
finished
finished
finished
finished

Process finished with exit code 0

* */

这样开销太大,同时过多的线程会占用太多的内存

创建和停止线程池

构造函数参数

corePollSize和maxPollSize

corePollSize:核心线程数,线程池在完成初始化之后,默认情况下线程池中没有任何线程,线程池会等待有任务时,再创建新线程执行任务

maxPollSize:线程池有可能会在核心线程数的基础上,额外增加一些线程,但是新增加的线程有数量上限,就是maxPollSize

添加线程规则
  1. 如果线程数小于corePoolSize,即使其他工作线程处于空闲状态,也会创建以恶搞新线程来执行
    新任务
  2. 如果线程数大于等于corePoolSize,小于maxmumPoolSize,将任务放入队列(workQueue)
  3. 如果队列已满,线程数小于maxmumPoolSize,创建一个新线程来执行任务
  4. 如果队列已满,线程数大于等于maxPollSize,就会拒绝任务

java并发-线程池_第1张图片

  • 如果设置corePollSize和maxPollSize相同,就可以创建固定大小的线程池
  • 线程池希望保持较少的线程数,只有在负载变得很大的时候才会增加线程
  • 通过设置maxPollSize为很高的值,就可以允许线程池容纳任意数量的并发任务
  • 只有队列满的时候才会创建多于corePollSize的线程,如果队列无界,那么线程数就不会超过corePollSize

keepAliveTime

如果线程池当前的线程数多于corePollSize,多余的线程空闲时间超过keepAliveTime就会被终止

ThreadFactory

新的线程是由ThreadFactory创建的,默认创建出来的线程都在一个线程组,优先级为NORM_PRIORITY,不是守护线程

如果是自己指定ThreadFactory,就可以改变线程名、线程组、优先级以及是否是守护线程

workQueue

三种常见的队列:

  1. 直接交换:SynchronousQueue
  2. 无界队列:LinkedBlockingQueue
  3. 有界队列:ArrayBlockingQueue

创建线程池

  • newFixedThreadPool

使用LinkedBlockingQueue

传进去的LinkedBlockingQueue没有容量上限。当请求越来越多,并且没有及时处理完毕时,会占用大量内存,可能会导致OOM

  • newSingleThreadExcetor
  • 使用LinkedBlockingQueue

和newFixedThreadPool的原理基本一致,只是将线程数设置为了1,也会导致和newFixedThreadPool相同的内存问题

  • newCachedThreadPool

使用SynchronousQueue

可缓存线程,无界线程池,具有回收多余线程的功能

corePollSize被设置为Integer.MAX_VALUE,这会导致创建非常多的线程,可能会导致OOM

  • newScheduledTheadPool

使用DelayedWorkQueue

支持定时、周期性执行任务

线程数量的设定

  • CPU密集型(加密、计算hash):最佳线程数为CPU核心数的1-2倍
  • 耗时IO型(读写数据库、文件、网络读写):最佳线程数一般大于CPU核心数很多倍,因为CPU的速度大于外设的速度,会产生空闲。要以JVM线程监控繁忙情况为依据
  • 线程数=CPU核心数*(1+平均等待时间 / 平均工作时间)

停止线程池

  • shutdown

执行之后,会将线程池存在的任务以及队列中等待的任务都执行完毕再停止,同时再有新任务时直接拒绝

  • isShutdown

会返回一个布尔值,告诉我们线程池是不是已经停止了(是不是进入停止的状态了而不是完全停止)

  • isTerminated

会返回一个布尔值,告诉我们线程池是不是完全停止了

  • awaitTermination

这个方法不是停止线程的。是等待一段时间,在这段时间内,如果线程停止了,就返回true,否则,返回false

  • shutdownNow

立刻停止线程池。对于正在执行的线程,会进行interrupt;对于还在队列中等待的线程会直接返回

如果不了解interrupt,可以去看我写的这一篇文章:java多线程-线程的停止【interrupt】_java多线程interrupt_健鑫.的博客-CSDN博客

任务的拒绝(学习一下是如何拒绝的,用到生活中 doge)

拒绝的时机

  1. 当Executor关闭时,提交的新任务会被拒绝
  2. 当Executor对最大线程和工作队列的容量使用有限边界并饱和时会拒绝

拒绝的策略

  • AbortPolicy

抛出异常

  • DiscardPolicy

直接把任务仍了,不会通知,所以你不知道任务得没得到处理

  • DiscardOldestPolicy

将队列中最老的任务丢弃

  • CallerRunsPolicy

谁提交的任务谁跑,让提交任务的线程去执行(老子没空,你牛逼你自己来)

你可能感兴趣的:(java并发编程,java,jvm,开发语言)