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

线程管理

时间:2023-04-02 01:19:51 Java

线程组线程未捕获异常和监控线程池线程组线程组(ThreadGroup)可以用来表示一系列相似或相关的线程集合。一个线程组可以包含多个线程和线程组。如果一个线程组中包含其他线程组,那么这个线程组就称为这些其他线程组的父线程组。如果一个线程没有关联到一个线程组,那么这个线程就是属于它的父线程所属的线程组。java虚拟机在创建主线程的时候会为其指定一个线程组,所以java中的任何一个线程都有一个关联的线程组。我们可以通过Thread.getThreadGroup()来获取与当前线程关联的线程组。未捕获的异常和线程的监控当一个线程在运行时发生未捕获的异常,会导致run方法退出,相应的线程也会被终止。对于这种情况,我们可以在线程启动之前将UncaughtExceptionHandler与线程相关联。当线程抛出未捕获的异常时,线程会在退出前调用UncaughtExceptionHandler.UncaughtException(Threadt,Throwablee)方法。我们可以使用这个方法进行相应的日志处理,或者重新创建一个线程来代替这个线程。publicclassUncaughtExceptionDemo{staticclassMyExceptionHandlerimplementsThread.UncaughtExceptionHandler{@OverridepublicvoiduncaughtException(Threadt,Throwablee){System.out.println(t.getName()+"线程抛出异常");MyThreadthread=newMyThread();thread.setName("B");thread.setUncaughtExceptionHandler(newMyExceptionHandler());thread.start();}}staticclassMyThreadextendsThread{@Overridepublicvoidrun(){if("A".equals(Thread.currentThread().getName())){thrownewBusinessException("出现异常:"+Thread.currentThread().getName());}System.out.println(Thread.currentThread().getName());}}staticclassBusinessExceptionextendsRuntimeException{publicBusinessException(Stringmessage){System.out.println(message);}}publicstaticvoidmain(String[]args){MyThreadmyThread=newMyThread();myThread.setName("A");myThread.setUncaughtExceptionHandler(newMyExceptionHandler());我的线程.start();}}============结果============异常:AA线程抛出异常B线程池使用线程池的好处减少资源消耗:有线程在可复用的线程池中使用的线程可以减少线程创建-销毁带来的性能消耗,提高线程响应速度:线程池中的线程直接响应任务,而不是响应后创建线程任务,有利于线程管理。线程池中的各个参数线程池中各个组件的职责和进程的含义。线程池管理器负责在线程池中创建、销毁、添加任务和其他工作线程任务队列。用来临时存放任务,可以起到缓冲的作用,因为一般是在并发场景下,所以任务队列一般使用BlockingQueue来保证线程安全的任务。该任务需要一个统一的接口,以便工作线程可以执行和处理什么是拒绝策略。线程池调用shutdown后,线程池会等待当前线程池中的所有线程执行完毕。但不会接受任何新任务。核心线程池已满-任务队列已满-线程池已达到最大线程数-线程池将不再接受新任务。几种常见的拒绝策略DiscardPolicy:新加入的任务会被直接忽略DiscardOldestPolicy:队列头部的任务会被移除,新的任务会插入队列的尾部可以在catch中重新处理CallerPolicy:当有新任务但线程池无法处理时,由提交任务的线程执行。(最全)普通线程池FixedThreadPool固定线程数的线程池,核心线程数与最大线程数相同newFixedThreadPool(10)-线程数从0开始递增beginning,增加到10后不会增加,有新的任务放入任务队列,有新任务时不会产生新的线程。CachedThreadPool线程池中的线程可以无限增加,但是不会超过Integr.MAX\_VALUE(2^31,几乎没达到)ScheduleThreadPool周期性执行任务SingleThreadExecutor只有一个线程执行任务,但是当当前线程异常时,该线程池将创建一个新线程来执行后续任务。好处是执行的任务是有序的。SingleThreadScheduleExecutor和3及其相似点是ScheduleThreadPool的一个特例,相当于newScheduledThreadPoolExecutor(1)ForkJoinPool经常用在递归的场景,比如遍历树RecursiveTask类是对ForkJoinTask的简单包装,那么我们重写compute()方法,当n<=1时,直接返回。当n>1时,创建递归任务,即f1和f2。然后我们使用fork()方法来拆分任务,分别执行。最后,在返回时,使用join()方法汇总结果。这样就实现了任务的划分和聚合。Fibonacci类扩展RecursiveTask{intn;publicFibonacci(intn){this.n=n;}@OverridepublicIntegercompute(){if(n<=1){返回n;}斐波那契f1=新斐波那契(n-1);f1.fork();斐波那契f2=新斐波那契(n-2);f2.fork();返回f1.join()+f2.join();}}publicstaticvoidmain(String[]args){ForkJoinPoolforkJoinPool=newForkJoinPool();for(inti=0;i<10;i++){ForkJoinTasktask=forkJoinPool.submit(newFibonacci(i));());为什么不推荐使用Executors创建线程池?下图是各个线程池使用的对应阻塞队列FixedThreadPool和SingleThreadExecutor。因为LinkBlockingQueue的容量几乎是无限的,如果任务的消费速度远远超过任务的生产速度,就会阻塞队列,堆积大量的任务可能会导致内存溢出。当CatchedThreadPool有很多任务时,线程池会创建无限制的新线程,最终可能导致操作系统无法创建新线程或导致内存溢出。ScheduleThreadPool和SingleThreadScheduleExecutorDelayedWorkQueue也是无界队列。如果队列中有大量任务,也可能会出现内存溢出。如何正确关闭线程池shutdown调用该方法后,线程池不会立即停止,而是在队列中的所有任务都处理完后才会彻底关闭。如果此时有新的任务提交,将被拒绝。shutdownNow立即终止线程池中的所有操作,这是不安全的。不建议使用isShutdown来判断shutdown方法是否已经执行。这个时候,线程池可能还在执行剩余的任务。isTerminated是否完全终止,即shoutdown执行完+剩余任务执行完或者shutdownNowawaitTerminationawaitTermination(10)-等待10秒,10秒内所有任务执行完返回true,10秒内未执行完返回false线程池监控ThreadPoolExecutor提供了线程池监控相关方法methodusegetPoolSize()获取当前线程池大小everreached,该值有助于确认线程池的最大大小是否合理。getActiveCount()获取线程池中当前正在执行任务的工作线程数(近似值)getTaskCount()获取线程池到目前为止接收到的任务数(近似值)getCompletedTaskCount()获取该线程正在执行的任务数到目前为止池已处理(近似值)