当前位置: 首页 > 后端技术 > Java

线程池个人总结

时间:2023-04-01 16:48:18 Java

学习线程池过程中出现的问题线程池的提交过程是怎样的?线程池是如何复用的?如何关闭线程池提交提交线程池有3种方式:Futuresubmit(Callabletask);Futuresubmit(Runnabletask,Tresult);Future<>提交(可运行任务);可以看到有2个Runnable和1个Callable提交,那为什么会有2个Runnable提交呢?通常我们认为Runnable和Callable的区别在于Callable可以返回一个结果。其实Runnable也是可以返回结果的。根据参数Tresult可以看出Tresult为返回结果。如果不传,默认返回NULL。接下来进入提交方法publicFuturesubmit(Callabletask){if(task==null)thrownewNullPointerException();RunnableFutureftask=newTaskFor(任务);执行(ftask);returnftask;}publicFuturesubmit(Runnabletask,Tresult){if(task==null)thrownewNullPointerException();}RunnableFutureftask=newTaskFor(任务,结果);执行(ftask);returnftask;}publicFuturesubmit(Runnabletask){if(task==null)thrownewNullPointerException();RunnableFutureftask=newTaskFor(task,null);执行(ftask);ftask;}这三个方法是先调用newTaskFor得到一个RunnableFuture,然后提交执行。protectedRunnableFuturenewTaskFor(Runnablerunnable,Tvalue){returnnewFutureTask(runnable,value);}protectedRunnableFuturenewTaskFor(Callablecallable){返回新的FutureTask(callable);}newTaskFor中构造的FutureTask就是我们用来获取执行状态和结果的适配类。在FutureTask类中,统一适配为Callable,只是Runnable返回NULL或者传入固定值。我们看一下ThreadPoolExecutor的执行过程:publicvoidexecute(Runnablecommand){if(command==null)thrownewNullPointerException();//1。判断是否在核心线程,如果在,则尝试创建核心线程intc=ctl.get();if(workerCountOf(c)=SHUTDOWN&&!(rs==SHUTDOWN&&firstTask==null&&!workQueue.isEmpty()))返回false;对于(;;){intwc=workerCountOf(c);如果(wc>=CAPACITY||wc>=(core?corePoolSize:maximumPoolSize))返回false;如果(compareAndIncrementWorkerCount(c))中断重试;c=ctl.get();//重新读取ctlif(runStateOf(c)!=rs)continueretry;//否则CAS因workerCount更改而失败;重试内循环}}booleanworkerStarted=false;布尔workerAdded=false;工人w=n空;try{//2、新建Worker来管理任务的执行,并将Worker添加到workers中管理w=newWorker(firstTask);最终线程t=w.thread;if(t!=null){finalReentrantLockmainLock=this.mainLock;主锁.lock();try{//持有锁时重新检查。//退出ThreadFactory失败或如果//在获取锁之前关闭。intrs=runStateOf(ctl.get());if(rslargestPoolSize)largestPoolSize=s;添加的工人=真;}}最后{mainLock.unlock();}if(workerAdded){//3.最后在执行步骤,直接运行start执行t.start();workerStarted=真;}}}finally{if(!workerStarted)addWorkerFailed(w);}returnworkerStarted;}前面说了参数booleancore控制添加核心/非核心线程,但是这个方法中只用core来判断,那么如何区分核心/非核心线程呢?答案是没有区别。兄弟二人在线程池上没有区别。你只需要考虑线程和内核的数量。最大数量可以直接运行启动。队列中的任务呢?这是在Worker内部解决的。Worker是一个内部类,实现了Runnable,它的执行逻辑都在runWorker中:可运行任务=w.firstTask;w.firstTask=null;w.unlock();//允许中断booleancompletedAbruptly=true;尝试{//1。循环获取任务,直到没有任务while(task!=null||(task=getTask())!=null){w.lock();//如果池正在停止,确保线程被中断;//如果不是,确保线程没有被中断。这//在第二种情况下需要重新检查以处理//shutdownNow竞争,同时清除中断if((runStateAtLeast(ctl.get(),STOP)||(Thread.interrupted()&&runStateAtLeast(ctl.get(),STOP)))&&!wt.isInterrupted())wt.interrupt();try{//2、Set处理前,默认实现为空beforeExecute(wt,task);Throwable抛出=null;尝试{//3。直接执行run,这样可以在同一个线程中执行不同的逻辑。任务.run();}catch(RuntimeExceptionx){抛出=x;扔x;}赶上(错误x){抛出=x;扔x;}catch(Throwablex){抛出=x;抛出新错误(x);}finally{//4、后处理,默认空执行afterExecute(task,thrown);}}最后{task=null;w.completedTasks++;w.解锁();}}completedAbruptly=false;}finally{5.RemoveWorkerprocessWorkerExit(w,completedAbruptly);}}我们可以猜测getTask是从队列中获取任务,当获取不到任务退出循环时,移除workerprivateRunnablegetTask(){booleantimedOut=false;//最后一次poll()是否超时?对于(;;){intc=ctl.get();intrs=runStateOf(c);//仅在必要时检查队列是否为空。if(rs>=SHUTDOWN&&(rs>=STOP||workQueue.isEmpty())){decrementWorkerCount();返回空值;}intwc=workerCountOf(c);//工人会被剔除吗?布尔计时=allowCoreThreadTimeOut||wc>核心池大小;if((wc>maximumPoolSize||(timed&&timedOut))&&(wc>1||workQueue.isEmpty())){if(compareAndDecrementWorkerCount(c))返回空;继续;}try{//如果需要判断超时时间,则在keepAliveTime时间后拉取任务,否则阻塞拉取任务Runnabler=timed?workQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS):workQueue.take();如果(r!=null)返回r;超时=真;}catch(InterruptedException重试){timedOut=false;}}}参数timed表示是否需要判断超时,timedOut表示如果当前线程数在核心线程数以内且核心线程允许超时,则是否超时,所以timed为false。这种情况下只有两种情况:空队列阻塞和任务返回;而如果当前线程数超过了核心线程,那么也有两种情况。队列有任务直接获取,队列没有任务则在keepAliveTime时间内等待获取,没有任务则结束执行。