[多线程]线程池

目录

1.前言

2. Java中的线程池以及参数介绍

2.1 核心线程数和最大线程数

2.2最大空闲存活时间

2.3任务队列和线程工厂

2.4 拒绝策略(最重要)

2.5 线程池的类型

3.线程池的大小如何确定

4.手动写一个线程池


1.前言

    我们知道.在开发过程中.为了效率,会引进很多池,比如常量池,对象池,字符串池.今天我们来介绍另一种可以管理线程的池,线程池.我们知道在多线程编程中,线程的开销是比较大的,如线程的摧毁和创建.为了解决这种问题,我们引入了线程池这个概念,它可以把部分线程的创建和销毁给省去了,而是直接放到特定的数据结构里,需要用的时候再拿出来.这样就可以极大的提升程序的效率

2. Java中的线程池以及参数介绍

  在Java中,也为我们封装了线程池的类,ThreadpollExecutor类,而这个类里面有几个重要的参数。它的构造方法有四种。             

[多线程]线程池_第1张图片    我们这里来重点介绍一下第四个构造方法,因为这个构造方法里的参数是最完整的。它包含了前面几个构造方法里的参数。

2.1 核心线程数和最大线程数

int  corePoolSize  核心线程数,相当于是正式的线程,正式工。

int maxmumPoolSize 最大线程数,除了核心线程,还有临时线程,相当于临时工,实习生。

2.2最大空闲存活时间

long keepAliveTime 实习生线程允许的最大空闲存活时间。 就是除了核心线程数之外的线程,在没有被利用到的时候,可以存在的时间。

TimeUnit unit 最大空闲存活时间单位,(s,hour,day。。。)

[多线程]线程池_第2张图片

2.3任务队列和线程工厂

BlockkingQueue workQueue 线程池里的任务队列,它是一个阻塞队列。

Threadfactry threadFactory 线程工厂

工厂模式可以解决Java语法的缺陷,就是在重载的时候,参数不能相同,但实际上,我们会有这种业务需求。比如,我们需要通过构造方法,来创建类,但是我们想传入的形参它的意义不同,但是类型和个数都一样。在Java基本语法中,是做不到的。但是我们可以通过把构造方法进行封装, 把它们放到工厂类里面去,通过工厂类来进行创建对象。

2.4 拒绝策略(最重要)

RejectedExecutionHandler  handler

  线程池中,会有一个阻塞队列。所以它能容纳的线程数量是有限的,当任务队列里的线程数满了以后,线程池会做出什么样的举动。这个是可以我们来控制一下的,所以就引入了 我们的拒绝策略。

[多线程]线程池_第3张图片

在标准库里,Java为我们提供了四种拒绝策略。

我们来逐个解释一下这些拒绝策略分别是什么:
 

ThreadPoolExecutor.AbortPolicy  

   抛出一个 RejectedExecutionException ,举个例子,就好比我现在在家做家务,手头有好几个活,我妈这时候让我在做一个新的家务,如果是这种拒绝策略。那就是我情绪崩溃了,哇的一声就哭出来了,然后撂挑子不干了。

ThreadPoolExecutor.CallerRunsPolicy  

新的任务,由添加任务的线程去执行。也就是我上述例子中,我给我妈说,我不干,你去做。

ThreadPoolExecutor.DiscardOldestPolicy 
被拒绝的任务的处理程序,丢弃最旧的未处理请求。就是把队列中,最老的那个线程给丢掉,然后加入这个最新的。就是上述例子中,我有一个最早的家务,比如是拖地,我就不去做这个了,而是把新家务给放到我的家务计划中。

ThreadPoolExecutor.DiscardPolicy 

直接丢掉最新的,不要别的线程加入,加不进去。就是我直接拒绝了给我安排的家务,并不会出现在我的家务计划列表中。

2.5 线程池的类型

Java标准库中,有这么四类线程池的类型。我们来通过图片来给大家看看。

[多线程]线程池_第4张图片

   如果只是简单用一下,就用这几种就可以了。如果需要高度定制化,那么就需ThreadPoolExecutor。来逐个设置参数了。阿里巴巴编程规范,要我们使用ThreadPoolExecutor。来让线程池更可控。可以参考一下。大家要以实际开发入职的编程规范为准。

3.线程池的大小如何确定

   网上有很多种关于线程池大小如何设定的说法,有的说如果cpu逻辑核心数是N,那么就应该设置为N个,1.5N.2N...但是这些说法都不严谨。

我们要看具体的代码里,线程中是cpu密集型的操作,还是io密集型的操作。如果是cpu密集型的操作,那么就设置为N就行,如果是io密集型的操作,这个时候对于cpu的消耗就比较小,我们就可以设置为远大于N的线程池大小。

但是在具体的工作开发中,这个东西是很难通过看代码来确定的,因为任务会很复杂,大概率是cpu和io同时都有,所以综上所述,最好的方法就是通过实验来确定。设置多大的合适,对于性能的提升最大。

实践是检验真理的唯一标准!

4.手动写一个线程池

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

 class MyThreadPoolExcutor {
    private BlockingQueue queue = new ArrayBlockingQueue<>(1000);
    private List list = new ArrayList<>();

    public MyThreadPoolExcutor(int a){

        for (int i = 0; i < a; i++) {
            Thread t =new Thread(()->{
                while (true){
                    try {
                       Runnable runnable =queue.take();
                       runnable.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

            });
            t.start();
            list.add(t);
        }
    }
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}
public class MyThreadExcutor {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPoolExcutor myThreadPoolExcutor = new MyThreadPoolExcutor(4);
        for (int i =0; i < 1000; i++) {
            int a= i;
            myThreadPoolExcutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("打印变量" + a +"执行的线程为" + Thread.currentThread().getName());
                }
            });
        }


    }
}

在上述代码里,我们手动创建了一个线程池,是固定线程的线程池。里面有一个带阻塞功能的任务队列,当我们put进去新的任务的时候,会进入到任务队列中。然后线程池里的线程就会执行这个任务,至于是哪个线程,是随机的一个。

这段代码实现了一个简单的线程池,能够并发执行多个任务,而不需要为每个任务都创建一个新的线程。

[多线程]线程池_第5张图片

                                                                                                                                                  

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