Java 线程池

目录

1.定义

2.优点

3.线程池的使用

3.1创建(分为七种):

3.2线程池终止:

3.3线程池执行的两种方式:


线程池

线程缺点:

  1. 线程的创建它会开辟本地方法栈、虚拟机栈、程序计数器成为线程私有的内存,同时销毁的时候需要销毁以上3个区域,因此频繁的创建和销毁比较消耗系统资源
  2. 在任务量远远大于线程可以处理的任务量的时候,并不能友好拒绝任务

1.定义

使用池化技术来管理线程和使用线程的方式

线程池有两个重要对象:

  1. 线程
  2. 工作队列(默认:Integer 最大值)

2.优点

  1. 可以避免频繁的创建和销毁线程
  2. 可以更好的管理线程的个数和资源的个数
  3. 线程池拥有更多的功能,比如线程池可以进行定时任务的执行
  4. 线程池可以更友好的拒绝不能处理的任务

3.线程池的使用

3.1创建(分为七种):

1.创建固定个数的线程池

        //创建一个固定个数的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //执行任务   
        for (int i = 0; i <2 ; i++) {//创建线程个数为实际应用来加载的
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }

 2.创建带缓存的线程池

        //创建带缓存的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }

使用场景: 短期有大量任务的时候使用CachedThreadPool

3.创建执行定时任务的线程池

方法1:

       //执行定时任务的线程池
        ScheduledExecutorService scheduledExecutorService 
              =Executors.newScheduledThreadPool(2);
        System.out.println("设置定时任务:"+new Date());
        //执行定时任务
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);
    

 运行结果

第一个参数:线程执行的任务 Runnable

第二个参数:延迟一段时间执行

第三个参数:定时任务执行的频率

第四个参数:配合参数2和参数3一起使用,

方法2:

        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },1,TimeUnit.SECONDS);

schedule只会执行一次

运行结果:

方法3:

       scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务:"+new Date());
            }
        },1,3,TimeUnit.SECONDS);

运行结果:

 

参数和方法1相同

两者的区别:

代码1:

       scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3, TimeUnit.SECONDS);

运行结果:

代码2:

       scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3,TimeUnit.SECONDS);

运行结果:

观察上面两个代码就可以知道两者区别:

  • scheduleAtFixedRate:以上次任务开始时间加上时间间隔作为下一次任务的开始时间
  • scheduleWithFixedDelay:以上次任务结束时间加上时间间隔作为下一次任务的开始时间

方法4:创建单个执行定时任务的线程池

        //创建单个执行任务的线程池
        ScheduledExecutorService scheduledExecutorService = 
               Executors.newSingleThreadScheduledExecutor();
        System.out.println("设置定时任务:"+new Date());
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("执行任务:"+new Date());
            }
        },1,3,TimeUnit.SECONDS);

方法5:创建单个线程池

        //创建单个线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
        }

运行结果:

经典面试题:创建单个线程池有什么用?

  1. 可以避免频繁创建和销毁线程带来的性能开销
  2. 有任务队列可以存储多余的任务
  3. 当有大量任务不能处理时,可以友好的执行拒绝策略
  4. 线程池可以更好的管理任务

以上方法在JDK8之前,以下方法在JDK8之后的版本才能使用

方法6:创建一个异步根据CPU生产的线程池

根据当前工作环境(硬件CPU核数和任务量)生成对应个数的线程池,并且是异步执行

        ExecutorService executorService=Executors.newWorkStealingPool();
        //创建一个异步根据CPU生产的线程池
        for (int i = 0; i < 10 ; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程名:"+Thread.currentThread().getName());
                }
            });
            //表示等待线程池执行完成

        }
        while (!executorService.isTerminated()){

        }

运行结果:

如果使用前6种创建线程的方式可能会导致的问题:

  1. 线程数不可控->OOM (outofmemorf):例如带缓存的线程池
  2. 工作任务队列不可控->OOM:例如创建固定个数的线程池(Integer.MAX_VALUE)

方法7:(最常用)原始线程池创建方式

 ThreadPoolExecutor threadPoolExecutor=
     new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10),threadFactory, new RejectedExecutionHandler() {
          @Override
          public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
             System.out.println("执行了拒绝策略");
      }
});
参数1:核心线程的数量 | 线程池正常情况下的数量
参数2:最大线程数量 | 当有大量任务的时候可以创建的最多的线程
参数3:最大线程存活时间
参数4:配合参数3使用,说明参数3的单位
参数5:任务队列
参数6:线程工厂|设置线程池命名规则、设置线程优先级····
参数7:拒绝策略 (https://blog.csdn.net/XKA_HRX/article/details/117089700)

注意事项:

1.最大线程数量必须大于核心线程的数量

2.ThreadPoolExecutor 执行流程:核心线程数、最大线程数、任务队列

1>当任务量小于核心线程数的时候,它会创建一个线程来执行此任务;

2>当任务量大于等于核心线程的时候,并且没有空闲线程时,

        会判断当前线程池的任务量是否大于等于最大线程数,判断当任务队列没满的时候,此时会将任务存到任务队列里面;

           (注意:因为把多余的任务存储在任务队列的成本最小,所以此时线程池会把任务存在任务队列中,而非创建新线程来执行任务)

        当任务量比较大的时候,此时没有空闲的线程,如果任务队列已经满了,此时会判断当前线程池的任务量是否大于等于最大线程数,

             如果当前线程池的线程数量小于最大线程数,就会创建新线程来执行任务;

             如果当前线程池的线程数量等于大于最大线程数,此时会执行拒绝策略;

3.2线程池终止:

threadPoolExecutor.shutDown():a.拒绝接受新任务  b.执行完任务队列之后进行关闭

threadPoolExecutor.shutDownNow():a.拒绝接受新任务  b.立即关闭

3.3线程池执行的两种方式:

excute(Runable 没有返回值)

submit(Runable /Callable 有返回值 Futrue<T>)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值