网赌网页>正规赌网址大全>勐拉爱发168·Java8线程池总结

勐拉爱发168·Java8线程池总结

作者:匿名 | 2020-01-08 08:07:57  | 阅读量:2868
no.2java中创建线程主要有三种方式1、继承thread类创建线程类定义thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。)使用futuretask对象作为thread对象的target创建并启动新线程。scheduledthreadpoolexecutor:scheduledexecutorservice 的实现,一个可定时调度任务的线程池。no.4

勐拉爱发168·Java8线程池总结

勐拉爱发168,no.1

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

no.2

java中创建线程主要有三种方式

1、继承thread类创建线程类

(1)定义thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run方法称为执行体。

(2)创建thread子类的实例,即创建了线程对象。

(3)调用线程对象的start方法来启动该线程。

2、通过runnable接口创建线程类

(1)定义runnable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体。

(2)创建 runnable实现类的实例,并以此实例作为thread的target来创建thread对象,该thread对象才是真正的线程对象。

(3)调用线程对象的start方法来启动该线程。

3、通过callable和future创建线程

(1)创建callable接口的实现类,并实现call方法,该call方法将作为线程执行体,并且有返回值。

public interface callable{

v call throws exception;

(2)创建callable实现类的实例,使用futuretask类来包装callable对象,该futuretask对象封装了该callable对象的call方法的返回值。(futuretask是一个包装器,它通过接受callable来创建,它同时实现了future和runnable接口。)

(3)使用futuretask对象作为thread对象的target创建并启动新线程。

(4)调用futuretask对象的get方法来获得子线程执行结束后的返回值

no.3

executor 框架

executor:一个接口,其定义了一个接收 runnable 对象的方法 executor,其方法签名为 executor(runnable command),

executorservice:是一个比 executor 使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回 future 的方法。

abstractexecutorservice:executorservice 执行方法的默认实现。

scheduledexecutorservice:一个可定时调度任务的接口。

scheduledthreadpoolexecutor:scheduledexecutorservice 的实现,一个可定时调度任务的线程池。

threadpoolexecutor:线程池,可以通过调用 executors 以下静态工厂方法来创建线程池并返回一个 executorservice 对象。

no.4

threadpoolexecutor的构造方法和参数

java线程的创建、销毁和线程减切换是一件比较耗费计算机资源的事。如果我们需要用多线程处理任务,并频繁的创建、销毁线程会造成计算机资源的无端浪费,因此出现了线程池技术。

使用线程池的好处:

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

线程池的最上层接口是executor,这个接口定义了一个核心方法execute(runnabel command),这个方法最后被threadpoolexecutor类实现,这个方法是用来传入任务的。而且threadpoolexecutor是线程池的核心类,此类的构造方法如下:

public threadpoolexecutor(int corepoolsize,int maximumpoolsize,long keepalivetime,timeunit unit,

blockingqueue workqueue); public threadpoolexecutor(int corepoolsize,int maximumpoolsize,long keepalivetime,timeunit unit,

blockingqueue workqueue,threadfactory threadfactory); public threadpoolexecutor(int corepoolsize,int maximumpoolsize,long keepalivetime,timeunit unit,

blockingqueue workqueue,rejectedexecutionhandler handler); public threadpoolexecutor(int corepoolsize,int maximumpoolsize,long keepalivetime,timeunit unit,

blockingqueue workqueue,threadfactory threadfactory,rejectedexecutionhandler handler);

其中的参数含义如下:

corepoolsize:核心线程池大小, 当新的任务到线程池后,线程池会创建新的线程(即使有空闲线程),直到核心线程池已满。

maximumpoolsize:最大线程池大小,顾名思义,线程池能创建的线程的最大数目

keepalivetime:程池的工作线程空闲后,保持存活的时间

timeunit:时间单位

blockingqueue

:用来储存等待执行任务的队列

threadfactory:线程工厂

rejectedexecutionhandler:当队列和线程池都满了时拒绝任务的策略

重要参数的说明:

corepoolsize 和 maximumpoolsize

默认情况下线程中的线程初始时为 0, 当有新的任务到来时才会创建新线程,当线程数目到达 corepoolsize 的数量时,新的任务会被缓存到workqueue 队列中。如果不断有新的任务到来,队列也满了的话,线程池会再新建线程直到总的线程数目达到 maximumpoolsize。如果还有新的任务到来,则要根据 handler 对新的任务进行相应拒绝处理。

blockingqueue一个阻塞队列,用来存储等待执行的任务,常用的有如下几种:

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

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

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

delayqueue(延迟队列):是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newscheduledthreadpool线程池使用了这个队列。

priorityblockingqueue(优先级队列):是具有优先级的无界阻塞队列;

rejectedexecutionhandler`当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。有下面四种jdk提供的策略:

threadpoolexecutor.abortpolicy:丢弃任务并抛出rejectedexecutionexception异常。

threadpoolexecutor.discardpolicy:也是丢弃任务,但是不抛出异常。

threadpoolexecutor.discardoldestpolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

threadpoolexecutor.callerrunspolicy:由调用线程处理该任务

流程:

no.5

executors

jdk 中提供了几种具有代表性的线程池,这些线程池是基于 threadpoolexecutor 的定制化实现。

常用方法有以下几个:

newfiexedthreadpool(int threads):创建固定数目线程的线程池。

newcachedthreadpool:创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果没有可用的线程,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

newsinglethreadexecutor创建一个单线程化的executor。

newscheduledthreadpool(int corepoolsize)创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代timer类。

但是在阿里巴巴java开发手册中也明确指出,而且用的词是『不允许』使用executors创建线程池。

no.6

创建线程池的正确姿势

避免使用executors创建线程池,主要是避免使用其中的默认实现,那么我们可以自己直接调用threadpoolexecutor的构造函数来自己创建线程池。在创建的同时,给blockqueue指定容量就可以了。

private static executorservice executor = new threadpoolexecutor(10, 10, 60l, timeunit.seconds, new arrayblockingqueue(10));

这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.rejectedexecutionexception,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(exception)总比发生错误(error)要好。除了自己定义threadpoolexecutor外。还有其他方法。这个时候第一时间就应该想到开源类库,如apache和guava等。

推荐使用guava提供的threadfactorybuilder来创建线程池。

public class executorsdemo { private static threadfactory namedthreadfactory = new threadfactorybuilder

.setnameformat("demo-pool-%d").build; private static executorservice pool = new threadpoolexecutor(5, 200, 0l, timeunit.milliseconds, new linkedblockingqueue(1024), namedthreadfactory, new threadpoolexecutor.abortpolicy); public static void main(string[] args) { for (int i = 0; i < integer.max_value; i++) {

pool.execute(new subthread);

通过上述方式创建线程时,不仅可以避免oom的问题,还可以自定义线程名称,更加方便的出错的时候溯源。

no.7

线程池状态

首先认识两个贯穿线程池代码的参数:

runstate:线程池运行状态

workercount:工作线程的数量

private final atomicinteger ctl = new atomicinteger(ctlof(running, 0)); private static final int count_bits = integer.size - 3; private static final int capacity = (1 << count_bits) - 1; // runstate is stored in the high-order bits

private static final int running = -1 << count_bits; private static final int shutdown = 0 << count_bits; private static final int stop = 1 << count_bits; private static final int tidying = 2 << count_bits; private static final int terminated = 3 << count_bits;

这个线程池的状态设计还是很有意思的,利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:

1、running:-1 << count_bits,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;

2、shutdown:0 << count_bits,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;

3、stop :1 << count_bits,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;

4、tidying :2 << count_bits,即高3位为010;

5、terminated:3 << count_bits,即高3位为011;

no.8

worker的创建

线程池是由worker类负责执行任务,调用execute将会根据线程池的情况创建worker,可以归纳出下图四种情况:

标记1对应第一种情况,要留意addworker传入了core,core=true为corepoolsize,core=false为maximumpoolsize,新增时需要检查workercount是否超过允许的最大值。

标记2对应第二种情况,检查线程池是否在运行,并且将任务加入等待队列。标记3再检查一次线程池状态,如果线程池忽然处于非运行状态,那就将等待队列刚加的任务删掉,再交给rejectedexecutionhandler处理。标记4发现没有worker,就先补充一个空任务的worker。

标记5对应第三种情况,等待队列不能再添加任务了,调用addworker添加一个去处理。

标记6对应第四种情况,addworker的core传入false,返回调用失败,代表workercount已经超出maximumpoolsize,那就交给rejectedexecutionhandler处理。

no.9

线程池callable任务执行原理

线程池的submit定义在abstractexecutorservice,根据入参的不同,有三个submit方法。下面以提交callable为例:

public future submit(callable task) { if (task == ) throw new pointerexception;

runnablefuture ftask = newtaskfor(task);

execute(ftask); return ftask;

}protected runnablefuture newtaskfor(callable callable) {

return new futuretask(callable);

futuretask在newtaskfor创建,然后调用线程池的execute执行,最后返回future。获取future后,就可以调用get获取结果,或者调用cancel取消任务。

future接口代表一个异步任务的结果,提供了相应方法判断任务是否完成或者取消。从图1可知,runnablefuture同时继承了future和runnable,是一个可运行、可知结果的任务,futuretask是具体的实现类。

private volatile int state;private static final int new = 0;private static final int completing = 1;private static final int normal = 2;private static final int exceptional = 3;private static final int cancelled = 4;private static final int interrupting = 5;private static final int interrupted = 6;

futuretask有7种状态,初始状态从new开始,状态转换路径可以归纳为下图

new -> completing -> normal 正常的流程

new -> completing -> exceptional 异常的流程

new -> cancelled 被取消流程

new -> interrupting -> interrupted 被中断流程

no.10

executorservice中submit和execute区别

1、execute是接口executor的方法,submit是executor子接口executorservice的方法2、接收参数不同

①submit可以接收runnable和callable,返回值future对象,但是runnable的返回值为void,所以future的 get方法返回。 ②execute只能接收runnable参数,返回值为void

3、返回值

①submit返回future,可以获取到线程返回的结果数据

②execute没有返回值

4、异常处理①execute直接抛出异常,在线程外部无法捕获异常,想要捕获该异常,可以实现uncaughtexceptionhandler接口

②submit不会抛出异常,需要调用返回值future对象的get方法

no.11

fork/join框架

java7 提供了forkjoinpool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合并成总的计算结果。

forkjoinpool是executorservice的实现类,因此是一种特殊的线程池。

使用方法:创建了forkjoinpool实例之后,就可以调用forkjoinpool的submit(forkjointasktask) 或invoke(forkjointasktask)方法来执行指定任务了。

其中forkjointask代表一个可以并行、合并的任务。forkjointask是一个抽象类,它还有两个抽象子类:recusiveaction和recusivetask。其中recusivetask代表有返回值的任务,而recusiveaction代表没有返回值的任务。

下面的uml类图显示了forkjoinpool、forkjointask之间的关系:

jdk1.8提供了创建forkjoinpool 的通用方法:forkjoinpool.commonpool;

举例用法:

@test

public void forkjointest {

forkjoinpool forkjoinpool = forkjoinpool.commonpool;

forkjointask forkjointask = forkjoinpool.submit(new counttask(1,1000)); try { int result = (int) forkjointask.get;

system.out.println(result);

} catch (interruptedexception e) {

e.printstacktrace;

} catch (executionexception e) {

e.printstacktrace;

} public class counttask extends recursivetask{ private static final int thread_hold = 2; private int start; private int end; public counttask(int start,int end){ this.start = start; this.end = end;

} @override

protected integer compute { int sum = 0; //如果任务足够小就计算

boolean cancompute = (end - start) <= thread_hold; if(cancompute){ for(int i=start;i<=end;i++){

sum += i;

}else{ int middle = (start + end) / 2;

counttask left = new counttask(start,middle);

counttask right = new counttask(middle+1,end); //执行子任务

left.fork;

right.fork; //获取子任务结果

int lresult = left.join; int rresult = right.join;

sum = lresult + rresult;

} return sum;

小e再次强烈建议

在系统上安装了itunes或icloud的windows用户

尽快将软件更新到最新版本!