这篇文章主要介绍“Java怎么用线程工厂来监控线程池”,在日常操作中,相信很多人在Java怎么用线程工厂来监控线程池问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java怎么用线程工厂来监控线程池”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

ThreadFactory

线程池中的线程从哪里来呢?就是ThreadFoctory

publicinterfaceThreadFactory{ThreadnewThread(Runnabler);}

Threadfactory里面有个接口,当线程池中需要创建线程就会调用该方法,也可以自定义线程工厂

publicclassThreadfactoryText{publicstaticvoidmain(String[]args){Runnablerunnable=newRunnable(){@Overridepublicvoidrun(){intnum=newRandom().nextInt(10);System.out.println(Thread.currentThread().getId()+"--"+System.currentTimeMillis()+"--睡眠"+num);try{TimeUnit.SECONDS.sleep(num);}catch(InterruptedExceptione){e.printStackTrace();}}};//创建线程池使用自定义线程工厂采用默认的拒绝策略ExecutorServiceexecutorService=newThreadPoolExecutor(5,5,0,TimeUnit.SECONDS,newSynchronousQueue<>(),newThreadFactory(){@OverridepublicThreadnewThread(Runnabler){Threadt=newThread(r);t.setDaemon(true);//设置为守护线程,当主线程运行结束,线程池中线程也会被释放System.out.println("创建了线程"+t);returnt;}});//提交五个任务for(inti=0;i<5;i++){executorService.submit(runnable);}}}

当线程提交超过五个任务时,线程池会默认抛出异常

监控线程池

ThreadPoolExcutor提供了一组方法用于监控线程池

intgetActiveCount()//获得线程池只当前的获得线程数量longgetCompletedTaskCount()//返回线程池完成任务数量intgetCorePoolSize()//线程池中核心任务数量intgetLargestPoolSize()//返回线程池中曾经达到线程的最大数intgetMaximumPoolSize()//返回线程池的最大容量intgetPoolSize()//返回线程大小BlockingQueue<Runnable>getQueue()//返回阻塞队列longgetTaskCount()//返回线程池收到任务总数

publicclassText{publicstaticvoidmain(String[]args)throwsInterruptedException{Runnablerunnable=newRunnable(){@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getId()+"线程开始执行--"+System.currentTimeMillis());try{Thread.sleep(10000);}catch(InterruptedExceptione){e.printStackTrace();}}};//创建线程池使用默认线程工厂有界队列采用DiscardPolicy策略ThreadPoolExecutorexecutorService=newThreadPoolExecutor(2,5,0,TimeUnit.SECONDS,newArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),newThreadPoolExecutor.DiscardPolicy());//提交五个任务for(inti=0;i<30;i++){executorService.submit(runnable);System.out.println("当前线程核心线程数"+executorService.getCorePoolSize()+",最大线程数:"+executorService.getMaximumPoolSize()+",当前线程池大小:"+executorService.getPoolSize()+"活动线程数:"+executorService.getActiveCount()+",收到任务:"+executorService.getTaskCount()+"完成任务数:"+executorService.getCompletedTaskCount()+"等待任务数:"+executorService.getQueue().size());TimeUnit.MILLISECONDS.sleep(500);}System.out.println("-------------------");while(executorService.getActiveCount()>=0)//继续对线程池进行检测{System.out.println("当前线程核心线程数"+executorService.getCorePoolSize()+",最大线程数:"+executorService.getMaximumPoolSize()+",当前线程池大小:"+executorService.getPoolSize()+"活动线程数:"+executorService.getActiveCount()+",收到任务:"+executorService.getTaskCount()+"完成任务数:"+executorService.getCompletedTaskCount()+"等待任务数:"+executorService.getQueue().size());Thread.sleep(1000);//每1秒检测一次}}}

当线程池大小达到了核心线程数,线程会被放在等待队列。当线程池等待队列已满会开启新的线程。当当前线程大小达到最大线程数,等待队列也满了,再提交的话会执行DiscardPolicy策略,直接丢弃这个无法处理的任务,最后30个任务只剩下15个了。

原理如图:

扩展线程池

有时候需要对线程池进行扩展,如在监控每个任务开始和结束时间,或者自定义其他增强功能。

ThreadPoolExecutor线程池提供了两个方法:

protectedvoidbeforeExecute(Threadt,Runnabler){}protectedvoidafterExecute(Runnabler,Throwablet){}

线程池执行某个任务前会执行beforeExecute()方法,执行后会调用afterExecute()方法

查看ThreadPoolExecutor源码,在该类中定义了一个内部类Worker,ThreadPoolExecutor线程池的工作线程就是Worker类的实例,Worker实例在执行时会调用beforeExecute与afterExecute方法。

publicvoidrun(){runWorker(this);}finalvoidrunWorker(Workerw){try{beforeExecute(wt,task);try{task.run();afterExecute(task,null);}catch(Throwableex){afterExecute(task,ex);throwex;}}finally{task=null;w.completedTasks++;w.unlock();}}}

部分代码已省略,线程执行前会调用beforeExecute,执行后会调用afterExecute方法。

扩展线程池示例

packagecom;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.LinkedBlockingDeque;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;publicclassText07{publicstaticvoidmain(String[]args){//定义扩展线程池定义线程池类继承ThreadPoolExecutor,然后重写其他方法ExecutorServicethreadPoolExecutor=newThreadPoolExecutor(5,5,0,TimeUnit.SECONDS,newLinkedBlockingDeque<>()){//在内部类重写开始方法@OverrideprotectedvoidbeforeExecute(Threadt,Runnabler){System.out.println(t.getId()+"线程准备执行任务"+((Mytask)r).name);}//在内部类重写结束方法@OverrideprotectedvoidafterExecute(Runnabler,Throwablet){System.out.println(((Mytask)r).name+"执行完成");}//线程池退出@Overrideprotectedvoidterminated(){System.out.println("线程池退出");}};for(inti=0;i<5;i++){Mytaskmytask=newMytask("Thread"+i);threadPoolExecutor.execute(mytask);}}privatestaticclassMytaskimplementsRunnable{privateStringname;publicMytask(Stringname){this.name=name;}@Overridepublicvoidrun(){System.out.println(name+"正在被执行"+Thread.currentThread().getId());try{Thread.sleep(1000);//模拟任务时长}catch(InterruptedExceptione){e.printStackTrace();}}}}

优化线程池大小

线程池大小对系统性能有一定影响,过大或者过小都无法方法发挥系统最佳性能,不需要非常精确,只要避免极大或者极小就可以了,一般来说线程池大小大姚考虑CPU数量

线程池大小=CPU数量 * 目标CPU使用率*(1+等待时间与计算时间的比)

线程池死锁

如果线程池执行中,任务A在执行过程中提交了任务B,任务B添加到线程池中的等待队列,如果A的结束需要B的执行结果,而B线程需要等待A线程执行完毕,就可能会使其他所有工作线程都处于等待状态,待这些任务在阻塞队列中执行。线程池中没有可以对阻塞队列进行处理的线程,就会一直等待下去照成死锁。

适合给线程池提交相互独立的任务,而不是彼此依赖的任务,对于彼此依赖的任务,可以考虑分别提交给不同的线程池来处理。

线程池异常信息捕获

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.SynchronousQueue;importjava.util.concurrent.ThreadPoolExecutor;importjava.util.concurrent.TimeUnit;publicclassText09{publicstaticvoidmain(String[]args){//创建线程池ExecutorServiceexecutorService=newThreadPoolExecutor(5,5,0,TimeUnit.SECONDS,newSynchronousQueue<>());//向线程池中添加两个数相处计算的任务for(inti=0;i<5;i++){executorService.submit(newText(10,i));}}privatestaticclassTextimplementsRunnable{privateintx;privateinty;publicText(intx,inty){this.x=x;this.y=y;}@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName()+"线程x/y结果的为"+x+"/"+y+"="+(x/y));}}}

可以看到只有四条结果,实际向线程池提交了五个任务,但是当i==0时,产生了算术异常,线程池把该异常吃掉了,导致我们对该异常一无所知

解决办法:

1.把submit改为execute

2.对线程池进行扩展,对submit进行包装

packagecom;importjava.util.concurrent.*;publicclassText09{publicstaticvoidmain(String[]args){//创建线程池使用自定义的线程池ExecutorServiceexecutorService=newTranceThreadPoorExcuter(5,5,0,TimeUnit.SECONDS,newSynchronousQueue<>());//向线程池中添加两个数相处计算的任务for(inti=0;i<5;i++){executorService.submit(newText(10,i));}}publicstaticclassTextimplementsRunnable{publicintx;publicinty;publicText(intx,inty){this.x=x;this.y=y;}@Overridepublicvoidrun(){System.out.println(Thread.currentThread().getName()+"线程x/y结果的为"+x+"/"+y+"="+(x/y));}}//自定义线程池类对TranceThreadPoorExcuter进行扩展privatestaticclassTranceThreadPoorExcuterextendsThreadPoolExecutor{publicTranceThreadPoorExcuter(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue){super(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue);}//定义一个方法用于传入两个参数第一个是要接受的任务第二个是ExceptionpublicRunnablewarp(Runnabler,Exceptione){returnnewRunnable(){@Overridepublicvoidrun(){try{r.run();}catch(Exceptione1){e.printStackTrace();throwe1;}}};}//重写submit方法@OverridepublicFuture<?>submit(Runnabletask){returnsuper.submit(warp(task,newException("客户跟踪异常")));}//还可以重写excute方法}}

此方法使用了自定义的线程池,重写线程池中的submit方法,在submit方法中,把要传入的任务参数带一个捕获异常信息的功能就可以捕获线程池异常。

到此,关于“Java怎么用线程工厂来监控线程池”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!