java——多线程之线程池

package thread;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;



public class ExecutorsDemo {
    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    		.setNameFormat("demo-pool-%d")
    		.build();
 
    /*
     * ThreadPoolExecutor(
  							int corePoolSize,
  							int maximumPoolSize,
  							long keepAliveTime,
  							TimeUnit unit,
  							BlockingQueue<Runnable> workQueue,
  							ThreadFactory threadFactory,
  							RejectedExecutionHandler handler
  						) 
 
	1.corePoolSize:表示线程池保有的最小线程数。
	2.maximumPoolSize:表示线程池创建的最大线程数。当项目很忙时,就需要加人,但是也不能无限制地加,最多就加到 maximumPoolSize 个人。当项目闲下来时,就要撤人了,最多能撤到 corePoolSize 个人。
	3.keepAliveTime & unit:上面提到项目根据忙闲来增减人员,那在编程世界里,如何定义忙和闲呢?很简单,一个线程如果在一段时间内,都没有执行任务,说明很闲,keepAliveTime 和 unit 就是用来定义这个“一段时间”的参数。
	4.workQueue :BlockingQueue主要有两种实现,分别是ArrayBlockingQueue 和 LinkedBlockingQueue。
	
			ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。
			LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。
			这里的问题就出在:**不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE。**也就是说,如果我们不设置LinkedBlockingQueue的容量的话,其默认容量将会是Integer.MAX_VALUE。
			 而newFixedThreadPool中创建LinkedBlockingQueue时,并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。

	5.threadFactory:通过这个参数你可以自定义如何创建线程,例如你可以给线程指定一个有意义的名字。
	6.handler:通过这个参数你可以自定义任务的拒绝策略。如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。至于拒绝的策略,你可以通过 handler 这个参数来指定。ThreadPoolExecutor 已经提供了以下 4 种策略。

		    CallerRunsPolicy:提交任务的线程自己去执行该任务。
		    AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。
		    DiscardPolicy:直接丢弃任务,没有任何异常抛出。
		    DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。
    */
    
    private static ExecutorService executor = new ThreadPoolExecutor(
    	5,                                          
    	200,									    
        0L,							
        TimeUnit.MILLISECONDS,	
        //new ArrayBlockingQueue<Runnable>(1024),
        new LinkedBlockingQueue<Runnable>(1024), 	
        namedThreadFactory, 						
        new ThreadPoolExecutor.AbortPolicy()		
        );
 

	   public static void main(String[] args) {		   
	      for(int i=0;i<10;i++) {
	    	  executor.execute(()-> System.out.println(Thread.currentThread().getName()));
	      }
	        
	        monitor();
	        executor.shutdown();//gracefully shutdown
	        monitor();
	    }
	   
	    private static void monitor() {
	        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
	        System.out.println("【线程池任务】线程池中曾经创建过的最大线程数:" + threadPoolExecutor.getLargestPoolSize());
	        System.out.println("【线程池任务】线程池中线程数:" + threadPoolExecutor.getPoolSize());
	        System.out.println("【线程池任务】线程池中活动的线程数:" + threadPoolExecutor.getActiveCount());
	        System.out.println("【线程池任务】队列中等待执行的任务数:" + threadPoolExecutor.getQueue().size());
	        System.out.println("【线程池任务】线程池已执行完任务数:" + threadPoolExecutor.getCompletedTaskCount()+"\r\n");
	    }
	   

  
}

 

https://segmentfault.com/a/1190000019558305

https://blog.csdn.net/hollis_chuang/article/details/83743723

使用线程池要注意些什么

考虑到 ThreadPoolExecutor 的构造函数实在是有些复杂,所以 Java 并发包里提供了一个线程池的静态工厂类 Executors,利用 Executors 你可以快速创建线程池。不过目前大厂的编码规范中基本上都不建议使用 Executors 了.

不建议使用 Executors 的最重要的原因是:Executors 提供的很多方法默认使用的都是无界的 LinkedBlockingQueue,高负载情境下,无界队列很容易导致 OOM,而 OOM 会导致所有请求都无法处理,这是致命问题。所以强烈建议使用有界队列

使用有界队列,当任务过多时,线程池会触发执行拒绝策略,线程池默认的拒绝策略会 throw RejectedExecutionException 这是个运行时异常,对于运行时异常编译器并不强制 catch 它,所以开发人员很容易忽略。因此默认拒绝策略要慎重使用。如果线程池处理的任务非常重要,建议自定义自己的拒绝策略;并且在实际工作中,自定义的拒绝策略往往和降级策略配合使用。

使用线程池,还要注意异常处理的问题,例如通过 ThreadPoolExecutor 对象的 execute() 方法提交任务时,如果任务在执行的过程中出现运行时异常,会导致执行任务的线程终止;不过,最致命的是任务虽然异常了,但是你却获取不到任何通知,这会让你误以为任务都执行得很正常。虽然线程池提供了很多用于异常处理的方法,但是最稳妥和简单的方案还是捕获所有异常并按需处理,你可以参考下面的示例代码。

try {
  // 业务逻辑
} catch (RuntimeException x) {
  // 按需处理
} catch (Throwable x) {
  // 按需处理
} 

 

如何使用 Java 中的线程池

Java 并发包里提供的线程池,远比我们上面的示例代码强大得多,当然也复杂得多。Java 提供的线程池相关的工具类中,最核心的是ThreadPoolExecutor,通过名字你也能看出来,它强调的是 Executor,而不是一般意义上的池化资源。
Java 并发包里提供的线程池,远比我们上面的示例代码强大得多,当然也复杂得多。Java 提供的线程池相关的工具类中,最核心的是

ThreadPoolExecutor(
  int corePoolSize,
  int maximumPoolSize,
  long keepAliveTime,
  TimeUnit unit,
  BlockingQueue<Runnable> workQueue,
  ThreadFactory threadFactory,
  RejectedExecutionHandler handler) 

1.corePoolSize:表示线程池保有的最小线程数。
2.maximumPoolSize:表示线程池创建的最大线程数。当项目很忙时,就需要加人,但是也不能无限制地加,最多就加到 maximumPoolSize 个人。当项目闲下来时,就要撤人了,最多能撤到 corePoolSize 个人。
3.keepAliveTime & unit:上面提到项目根据忙闲来增减人员,那在编程世界里,如何定义忙和闲呢?很简单,一个线程如果在一段时间内,都没有执行任务,说明很闲,keepAliveTime 和 unit 就是用来定义这个“一段时间”的参数。
4.workQueue :工作队列,和上面示例代码的工作队列同义。
5.threadFactory:通过这个参数你可以自定义如何创建线程,例如你可以给线程指定一个有意义的名字。
6.handler:通过这个参数你可以自定义任务的拒绝策略。如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。至于拒绝的策略,你可以通过 handler 这个参数来指定。ThreadPoolExecutor 已经提供了以下 4 种策略。

  • CallerRunsPolicy:提交任务的线程自己去执行该任务。
  • AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。
  • DiscardPolicy:直接丢弃任务,没有任何异常抛出。
  • DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值