线程池一系列面试问题

 线程是非常宝贵的计算资源,创建和销毁线程花费大量的时间和资源

线程池就是为了实现线程复用

线程池工作流程(工作原理):

总结:先是用核心线程去执行任务,核心线程满了就去队列中等(如果核心线程空闲了,就回会去执行阻塞队列中的任务),队列也满了就创建非核心线程去执行任务,达到最大线程数,再来任务就拒绝

核心线程-----阻塞队列-------非核心线程

 任务进来之后,先检查一下核心线程是否全部在执行任务(如果不是全部在执行任务,就用核心线程去执行任务)

如果核心线程全部已经在执行任务了,就将任务放到任务队列当中去

如果任务队列也满了,就检查一下正在执行任务的线程是否达到最大线程数,如果没有达到最大线程数,就创建非核心线程执行任务

如果任务队列也满了,正在执行任务的线程数又达到了最大线程数,就会采用饱和策略进行任务拒绝(四种拒绝策略)

没有任务时,线程池里的核心线程处于空闲状态

当请求到来:线程池给这个请求分配一个空闲的核心线程,任务完成后回到线程池中等待下次任务(而不是销毁)

创建线程池的几种方式:主要有两种方式Executors,ThreadPoolExecutor

ExecutorService executorService1 =  Executors.newFixedThreadPool(10);


ExecutorService executorService2 =  new ThreadPoolExecutor(3,5,1L,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

通过ThreadPoolExecutor的方式需要设定7个参数:

 线程池七大参数:

1.corePoolSize:        核心线程数的大小(核心线程永远不会被销毁)

2.maximumPoolSize:线程池最大线程数

3.keepAliveTime:生存时间,空闲时间(非核心线程如果超过生存时间没有执行任务,就会被销毁)

4.TimeUnit.SECONDS:生存时间的单位,可以是秒也可以是小时.......

5.ArraryBlockQueue:任务队列(核心)

6.Executors.defaultThreadFactory:线程工厂,用来创建线程

7.拒绝策略

主要有四个拒绝策略:

(1)AbortPolicy:线程池的默认策略,拒绝并且抛出RejectedExecutionException异常

(2)CallerRunsPolicy:虽然线程池自己拒绝了这个任务,但是主线程会去执行这个任务

(3)DiscardPolicy:直接丢掉这个任务,不会有任何异常

(4)DiscardOldestPolicy:如果队列满了,将最早进入队列的任务删掉腾出空间,再将新任务加进队列  

也可以自己自定义拒绝策略

阿里巴巴java开发手册规定:线程池不允许使用Executors去创建,而是需要通过ThreadPoolExecutor来创建,因为通过设置七个参数可以让同学们更加明确线程池的运行规则

 这里总共来了100个任务,3个核心线程,最大线程数为5

ExecutorService executorService=new ThreadPoolExecutor(3,5,1L,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

        for(int i=0;i<100;i++)
        {
            executorService.execute(()->
                                    {
                                        System.out.println( Thread.currentThread().getName()+"  正在办理业务");
                                    } );
        }

        executorService.shutdown();

 可以看到最多只有五个线程在工作

   //step 1:创建大小为10的线程池
        ExecutorService threadPool  =  Executors.newFixedThreadPool(10);

        for(int  i=0  ; i<10; i++)
        {
            //step 2:提交多个线程任务并执行
            threadPool.execute(new  Runnable()
                                  {
                                        @Override
                                        public  void  run()
                                        {
                                            System.out.println(Thread.currentThread().getName()  +  "   is  running");
                                        }
                                  }
                                );
        }

 输出:

pool-1-thread-1   is  running
pool-1-thread-2   is  running
pool-1-thread-3   is  running
pool-1-thread-4   is  running
pool-1-thread-5   is  running
pool-1-thread-6   is  running
pool-1-thread-9   is  running
pool-1-thread-7   is  running
pool-1-thread-10   is  running

pool-1-thread-8   is  running

如何定义线程池的大小:

1.使用经验值来定义线程池大小

线程池可以分为两种类型:

(1)I/O密集型(大部分进行I/O操作,CPU占用率不高,例如mysql的读写,文件的读写,网络通信,这些操作不会消耗太多的cpu资源,但是I/O操作特别耗时,会占用比较多的时间,此时CPU相对比较空间,为了更好的利用cpu,不让cpu空闲下来,所以应该加大线程数量,建议线程池大小=2N+1,其中N为CPU的个数)

(2)CPU密集型(也叫计算任务密集型,几乎没有I/O操作,CPU使用时间远高于I/O时间,也就是计算的时候要执行很多逻辑判断,因此频繁的切换上下文是不明智的,所以设置一个较小的线程数,建议线程池大小设为N+1)

通过下面的方法可以拿到当前机器的cpu核数:

Runtime.getRuntime().availableProcessors();//8

2.使用公式来定义线程池大小

最佳线程数=cpu数目* (线程等待时间+线程cpu时间)/线程cpu时间    

所以线程等待时间占比越高,就需要越多线程

虽然这个公式比较准确,但是实际中线程等待时间不好测量,所以公式法用的少,还是用经验值方法用的多

先创建一个线程池,然后用这个线程池去执行线程,每execute()一次就是执行一次线程

public class testCountDownLatch {
    public static void main(String[] args) throws InterruptedException {

        ExecutorService threadPool= Executors.newFixedThreadPool(5);//创建大小为5的线程池

      
            threadPool.execute( ()->{
                    System.out.println("执行线程1");
            });
              
             threadPool.execute( ()->{
                    System.out.println("执行线程2");
            });
            
            threadPool.execute( ()->{
                    System.out.println("执行线程3");
            });

            threadPool.execute( ()->{
                    System.out.println("执行线程4");
            });

            threadPool.execute( ()->{
                    System.out.println("执行线程5");
            });

            threadPool.execute( ()->{
                    System.out.println("执行线程6");
            });
      
         threadPool.shutdown();//销毁线程池
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值