线程池的基础与操作

一、概念

使用ThreadPoolExecutor类:包含了五个参数int corePoolSize(线程池的基本大小),int maximumPoolSize(最大线程池容量),long keepAliveTime(存活时间),TimeUnit unit(时间单位), BlockingQueue<Runnable> workQueue(任务队列)

    corePoolSize:当线程池刚刚创建的时候,线程池中没用线程,当有任务创建且当前线程数小于corePoolSize时才会去创建新的线程。但是当调用prestartAllCoreThreads或prestartCoreThread方法时候才会在创建时就创建corePoolSize个或一个线程。当创建的线程数等于corePoolSize时,就不再创建新线程,而是放到缓存队列中,当缓存队列(缓存队列的大小通过BlockingQueue来设置)满了的时候,才开始创建新的线程。

    maximumPoolSize:线程创建的最大数,当线程数未达到该值,并且任务队列已满,就可以新创建线程。

             当线程达到maximumPoolSize,并且队列也已经满了,如果再有新任务就会采取饱和策略,默认采取AbortPolicy,用来抛出异常,表示不能再处理新的任务。

             有四种策略:

              

  1. CallerRunsPolicy:只用调用者所在线程来运行任务。
  2. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
  3. DiscardPolicy:不处理,丢弃掉。
  4. 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

    keepAliveTime:线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率,避免重复的创建销毁线程。

    TimeUnit:时间单位:线程keepAliveTime的时间单位

    BlockingQueue:任务缓存队列,用于保存线程数达到corePoolSize之后的阻塞队列。可以选择以下几种阻塞队列:

    1.ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

    2.LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

    3.SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

    4.PriorityBlockingQueue:一个具有优先级得无限阻塞队列。

流程图:

二、操作

  1、线程池创建:

ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));

  2、使用线程池执行任务

//execute中的参数是一个runnable接口实例
threadsPool.execute(new Runnable() {
public void run(){
    }
}

//也可以使用一个实例先实现runnable
Class task implements Runnable(){
     public void run(){

    }
}       

threadsPool.execute(task);

3、简单的线程例子

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));

        for(int i=0;i<15;i++){
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                    executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
        }
        executor.shutdown();
    }
}

class MyTask implements Runnable {
    private int taskNum;

    public MyTask(int num) {
        this.taskNum = num;
    }

    @Override
    public void run() {
        System.out.println("正在执行task "+taskNum);
        try {
            Thread.currentThread().sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task "+taskNum+"执行完毕");
    }
}

  4、线程执行任务有两个方法:submit和execute

   不同点:

    1、submit接受的参数可以是callable接口实例,runnable接口实例;而execute接受的参数只有runnable接口实例

    2、对待异常的处理不同,execute和正常的一样抛出异常,而submit如果不是用get方法的时候就会吃掉异常,返回的future对象使用get方法后才会打印简略异常。

    3、submit的返回的future对象使用get方法由于需要等待接收任务的返回值所以会阻塞线程池,同步阻塞执行任务,任务执行成功,就返回一个null。

三、线程池的合理配置考虑

根据任务的不同进行分门别类的处理:

  1、任务的性质不同:io密集型任务、cpu运算密集型任务、混合型任务

  2、优先级不同:高中低

  3、需要时间不同的任务:短中长

  4、是否有依赖

  

  1、性质不同: 如果是cpu运算密集型的任务的话,由于运算快,线程的利用率更高,所以尽可能少的设置最大线程数,cpu+1个为好

          如果是io存储密集型任务的话,由于存储时io较为耗时,线程利用率更低,所以设置2*task个线程为好。

  2、时间不同和优先级不同以及有否依赖都可以通过使用优先级队列来解决

    时间短的优先级大些先完成,优先级高的也将其设置为高优先级,依赖底层的也设置高优先级

风险:建议使用有界队列,如果使用无界队列,一旦任务耗时严重,线程达到了线程corepoolsize,就会往任务队列里存放,如果一直存放的话会造成内存溢出oom异常。

原文地址:https://www.cnblogs.com/television/p/9402520.html

时间: 2024-10-10 19:40:21

线程池的基础与操作的相关文章

java面试总躲不过的并发(一): 线程池ThreadPoolExecutor基础梳理

本文核心:线程池ThreadPoolExecutor基础梳理 一.实现多线程的方式 1.继承Thread类,重写其run方法 2.实现Runnable接口,实现run方法 3.实现Callable接口,实现call方法 由于Java的设计,只支持单继承,但是支持多实现形式,所以一般面向接口开发,Runnable接口与Callable接口的区别在于Callable接口中的call方法是带返回值的,其返回一个Future的异步类,我们可以通过Future的get方法获取结果,如果线程还没有执行完,g

Java线程池的基础应用

了解:原子性操作类的基础用法 在java5以后,增加很多线程的内容,成为线程得并发库. 在java.util.concurrent包及子包的API帮助文档,上面有对并发库涉及内容 在java.util.concurrent.atomic包,有对基本数据,对数组中的基本数据,类中的基本数据等的操作 AtomicIneger类,可以解决多线程访问整数的问题 AtomicBoolean..... AtomicArray...操作数组的基本数据 AtomicIntegerFieldUodater<T>

深入浅出 Java Concurrency (36): 线程池 part 9 并发操作异常体系[转]

并发包引入的工具类很多方法都会抛出一定的异常,这些异常描述了任务在线程池中执行时发生的例外情况,而通常这些例外需要应用程序进行捕捉和处理. 例如在Future接口中有如下一个API: java.util.concurrent.Future.get(long, TimeUnit) throws InterruptedException, ExecutionException, TimeoutException; 在前面的章节中描述了Future类的具体实现原理.这里不再讨论,但是比较好奇的抛出的三

Java线程池ThreadPoolExector的源码分析

前言:线程是我们在学习java过程中非常重要的也是绕不开的一个知识点,它的重要程度可以说是java的核心之一,线程具有不可轻视的作用,对于我们提高程序的运行效率.压榨CPU处理能力.多条线路同时运行等都是强有力的杀手锏工具.线程是如此的重要,那么我们来思考这样一个问题.假设我们有一个高并发,多线程的项目,多条线程在运行的时候,来一个任务我们new一个线程,任务结束了,再把它销毁结束,这样看似没有问题,适合于低并发的场景,可是当我们的项目投入到生产环境,一下涌入千条任务的时候,线程不断的new执行

(CLR via C#学习笔记)异步操作 - 线程池

一 线程池基础 1.线程池维护了一个操作请求队列,将请求的操作追加到线程池队列中,线程池的代码从队列中提取操作项,派发给线程池中的线程; 2.CLR初始化时,线程池中是没有线程的,当有操作派发给线程池时,如果线程池中没有线程或者没有空闲状态的线程,将会创建一个新的线程执行派发的操作,如果有空闲状态的线程,将直接派发一个空闲状态的线程执行操作; 3.线程池线程完成操作任务后,线程不会被销毁,而是返回线程池,进入空闲状态,等待响应另一个派发请求;4.当一个线程池线程处于空闲状态一段时间后(不同的CL

论线程池的线程粒度划分与深浅放置

摘要:多线程任务处理对提高性能很有帮助,在Java中提供的线程池也方便了对多线程任务的实现.使用它很简单,而如果进行了不正确的使用,那么代码将陷入一团乱麻.因此如何正确地使用它,如以下分享,这个技能你get到没? 关键词:多线程, 线程池, 数据库, 算法 解决问题:如何正确使用线程池. 众所周知,线程池在Java中非常常用,使用它也是一项最基本的技能.不过怎样才能更合理.更方便地使用线程池,我们需要总结一下. 下面是线程池最基础的使用方式. ExecutorService jobPool =

07深入理解Java线程池

之前面试baba系时遇到一个相对简单的多线程编程题,即"3个线程循环输出ADC",自己答的并不是很好,深感内疚,决定更加仔细的学习<并发编程的艺术>一书,到达掌握的强度.(之前两月休息时间都花在了lol和吃鸡上,算是劳逸结合了,推荐大家代码写累了可以玩下吃鸡,是个不错的调剂) 流程分析 Java的线程池是最常用的并发框架,合理的使用线程池可以降低系统消耗.提高响应速度.提高线程的可管理性.线程池的基础处理流程如下图所示. 上图中标红的4处正好是构建线程池的核心,核心线程池大

【Java并发编程】21、线程池ThreadPoolExecutor源码解析

一.前言 JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法.下面开始分析. 二.ThreadPoolExecutor数据结构 在ThreadPoolExecutor的内部,主要由BlockingQueue和AbstractQu

J.U.C之线程池

先看一下线程池的基础架构图: Executor Executor,任务的执行者,线程池框架中几乎所有类都直接或者间接实现Executor接口,它是线程池框架的基础.Executor提供了一种将"任务提交"与"任务执行"分离开来的机制,它仅提供了一个Execute()方法用来执行已经提交的Runnable任务. public interface Executor { /** * Executes the given command at some time in th