java线程池的用法与建议

      小白向大神进阶的阶段,线程/JUC/并发是不可避免要学习和掌握的。那么我们这篇文章就来说说,什么是线程池,线程池又是怎么使用的?使用的时候,我们又需要注意些什么呢?

 

一,线程池的简介

    线程池顾名思义,是专门用来放置线程的容器。避免了线程在代码运行期间的不断地创建以及销毁损耗时间,提高了系统的性能,也提高的执行效率。可能对于没有用过的人来说,线程池是比较难以上手或者理解的,其实并不是这样,当你揭开神秘的面纱时,线程池也就很好上手了。

 

二,线程池的基本参数以及使用

      1,线程池的基本参数

        线程池维护核心线程的最大数量:线程池里面最大只能一直存活的核心线程数

        线程池维护的最大线程数量:这个最大数量和核心线程池最大数量不是一个概念,你可以这样理解(最大核心线程=<线程池最大线程数量)

         非核心线程的最大存活时间:就是非核心线程在线程池的允许存活时间

    线程池维护线程所允许的空闲时间的单位:这个就是时间单位,分,秒,...

  注:还有其他核心的参数,后面会详细的介绍,这里就不占用篇幅了

     2,线程池的用法

          2.1 线程池的第一种阻塞队列:直接提交队列

     线程池的创建,创建方式代码呈上。

      

/**
 * 线程池的工具类
 * <p>
 * </p>
 *
 * @author libing
 * @since 2019-07-18 上午 10:07
 */
@Slf4j
@Component
public class ThreadPoolUtils {


    /**
     * 线程池维护核心线程的最大数量
     */
    public static final int CORE_POOL_SIZE = 5;

    /**
     * 线程池维护的最大线程数量
     */
    public static final int MAX_IMUN_POOL_SIZE = 10;

    /**
     * 线程池维护线程所允许的空闲时间,当线程数大于核心线程时,此为终止前多余的空闲线程等待新任务的最长时间
     */
    public static final long KEEP_ALIVE_TIME = 5;

    /**
     * 线程池维护线程所允许的空闲时间的单位
     */
    private TimeUnit seconds = TimeUnit.SECONDS;

 /**
     * 线程池的第一种阻塞队列:直接提交队列
     */
    final static SynchronousQueue<Runnable> synchronousQueue = new SynchronousQueue<Runnable>();

/**
     * ThreadFactoryBuilder 使用这个符合阿里巴巴的开发手册.
     */
    final static ThreadFactory buildFactory = new ThreadFactoryBuilder().setNameFormat("liBing_pool_%d").build();


 /**
     * 线程池的第一种任务队列:直接提交队列
     * <p>
     * 注:这种情况,如果线程创建数超过最大线程数,则后面的任务则会执行拒绝策略为AbortPolic策略,直接抛出异常
     */
    public void synchronousQueue() {
        //创建线程池并且设置相应的参数
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_IMUN_POOL_SIZE,
                KEEP_ALIVE_TIME,
                seconds,
                synchronousQueue,
                buildFactory,
                abortPolicy);
        for (int i = 1; i <= 11; i++) {
            MyThread thread = new MyThread();
            threadPoolExecutor.execute(thread);
            System.out.println("线程池中现在的线程数目是:" + threadPoolExecutor.getPoolSize() + ",  队列中正在等待执行的任务数量为:" +
                    threadPoolExecutor.getQueue().size() + ",已经执行完的任务数量:" + threadPoolExecutor.getCompletedTaskCount());
        }
    }





}

          这是第一种方式,首先线程池的创建基本格式就是这样。前四个参数很好理解,最大核心线程数,线程池最大线程数,非核心线程数的存活时间,存活时间的单位。真正核心的就是后面几个参数。

         第五个参数(synchronousQueue)就是阻塞队列的直接提交队列。说到队列,那么我们就有必要说说,这个线程是怎么放进去的,数量又是如何控制的。打个不恰当的比方,放到线程池的你可以理解是一个个的待执行的任务,先进核心线程,达到最大的核心线程数量的时候,我们会将任务放在队列中,等队列满了,我们会将任务放到非核心线程中。简而言之,先核心,后队列,再非核心。直接提交队列可以理解为队列的size=0;

       阻塞队列有四种

      1,直接提交队列  :相当于没有队列。size =0

/**
     * 线程池的第一种阻塞队列:直接提交队列
     */
    final static SynchronousQueue<Runnable> synchronousQueue = new SynchronousQueue<Runnable>();

  2,有界的任务队列,size是new出来的后面(10),这就是size=10,队列可以放10个线程任务

/**
     * 线程池的第二种阻塞队列:有界的任务队列
     */
    final static ArrayBlockingQueue<Runnable> arrayBlockingQueue= new ArrayBlockingQueue<Runnable>(10);

   3,无界的任务队列,就是队列无限大,但是这个后面也是可以放size的,放了就是填写的size值(已验证)

 /**
     * 线程池的第三种阻塞队列:无界的任务队列
     * 使用无界任务队列,线程池的任务队列可以无限制的添加新的任务
     * 要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题
     */
    final static LinkedBlockingQueue<Runnable> linkedBlockingQueue= new LinkedBlockingQueue<Runnable>(10);

    final static LinkedBlockingQueue<Runnable> linkedBlockingQueue= new LinkedBlockingQueue<Runnable>();

4,线程池的第四种阻塞队列:优先级队列,就是队列里面会进行任务优先级的比较,优先级大先执行,前面几种但是先进先出的模式

/**
     * 线程池的第四种阻塞队列:优先级队列
     */
    final static PriorityBlockingQueue<Runnable> priorityBlockingQueue= new PriorityBlockingQueue<Runnable>();

  

 

      现在你对于队列应该有一个大概的了解了吧。那么我们说下第六个参数(buildFactory)。这个参数创建线程池可以自定义命名线程池的名称,方便回溯问题,同时阿里巴巴开发手册一直强调建议使用这样的写法。代码上面有,我这里再贴一遍

 /**
     * ThreadFactoryBuilder 使用这个符合阿里巴巴的开发手册.
     */
    final static ThreadFactory buildFactory = new ThreadFactoryBuilder().setNameFormat("liBing_pool_%d").build();

第七个参数(abortPolicy),这是默认的拒绝策略。核心线程数满了,队列满了,非核心线程也满了,还有线程任务怎么办,这个时候就会默认执行拒绝策略,后面的进来的线程任务全部不执行同时终止程序。

   拒绝策略有四种:

1、AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;

2、CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;

3、DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;

4、DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

 

测试代码:

    /**
     *
     *线程池的第一种任务队列:直接提交队列
     *
     */
    @Test
    public void synchronousQueue (){
        threadPoolUtils.synchronousQueue();
        log.info("验证程序是否正常运行");
    }

另外说下,我这边测试的时候核心线程是5,线程池最大是10,队列是直接提交队列(就是所谓的size=0)

这个不能很明显的验证我们之前说的,先核心,后队列,再非核心的存储方式,下面会一个个贴出来跑的结果。

  2.2 线程池的第二种阻塞队列:有界的任务队列

 

   /**
     * 线程池的第二种阻塞队列:有界的任务队列
     *
     */
    public void arrayBlockingQueue() {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_IMUN_POOL_SIZE,
                KEEP_ALIVE_TIME,
                seconds,
                arrayBlockingQueue,
                buildFactory,
                abortPolicy);
        for (int i = 1; i <= 33; i++) {
            MyThread thread = new MyThread();
            threadPoolExecutor.execute(thread);
            System.out.println("线程池中现在的线程数目是:" + threadPoolExecutor.getPoolSize() + ",  队列中正在等待执行的任务数量为:" +
                    threadPoolExecutor.getQueue().size() + ",已经执行完的任务数量:" + threadPoolExecutor.getCompletedTaskCount());
        }
        try {
            String name = Thread.currentThread().getName();
            log.info("【{}】线程睡眠开始", name);
            Thread.sleep(5000);
            log.info("【{}】线程睡眠结束", name);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("循环结束,线程池中现在的线程数目是:" + threadPoolExecutor.getPoolSize() + ",  队列中正在等待执行的任务数量为:" +
                threadPoolExecutor.getQueue().size() + ",已经执行完的任务数量:" + threadPoolExecutor.getCompletedTaskCount());
    }

这里,同样我们验证临界值之外的情况。我这边测试的时候核心线程是5,线程池最大是10,队列是有界的任务队列(size=10)

/**
     *
     *线程池的第二种任务队列:直接提交队列
     *
     */
    @Test
    public void arrayBlockingQueue (){
        threadPoolUtils.arrayBlockingQueue();
        log.info("验证程序是否正常运行");
    }

看到这样的情况我想不用说了吧,控制台展示的很明显。先核心,后队列,再非核心的存储方式。

2.2 线程池的第三种阻塞队列:无界的任务队列

  /**
     * 线程池的第三种任务队列:无界的任务队列
     * <p>
     * 注:这种情况,如果线程创建数超过最大线程数,则后面的任务则会执行拒绝策略为AbortPolic策略,直接抛出异常
     */
    public void linkedBlockingQueue() {
        //创建线程池并且设置相应的参数
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_IMUN_POOL_SIZE,
                KEEP_ALIVE_TIME,
                seconds,
                linkedBlockingQueue,
                buildFactory,
                abortPolicy);
        for (int i = 1; i <= 100; i++) {
            MyThread thread = new MyThread();
            threadPoolExecutor.execute(thread);
            System.out.println("线程池中现在的线程数目是:" + threadPoolExecutor.getPoolSize() + ",  队列中正在等待执行的任务数量为:" +
                    threadPoolExecutor.getQueue().size() + ",已经执行完的任务数量:" + threadPoolExecutor.getCompletedTaskCount());
        }
    }

测试代码

 /**
     *
     *线程池的第三种任务队列:无界的任务队列
     *
     */
    @Test
    public void linkedBlockingQueue (){
        threadPoolUtils.linkedBlockingQueue();
        log.info("验证程序是否正常运行");
    }

不建议使用无界的,因为容易无限制消耗系统的内存,容易把系统整挂了。

2.2 线程池的第四种阻塞队列:优先级任务队列

  /**
     * 线程池的第四种任务队列:优先任务队列
     * <p>
     * 注:这种情况,如果线程创建数超过最大线程数,则后面的任务则会执行拒绝策略为AbortPolic策略,直接抛出异常
     */
    public void priorityBlockingQueue() {
        //创建线程池并且设置相应的参数
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_IMUN_POOL_SIZE,
                KEEP_ALIVE_TIME,
                seconds,
                priorityBlockingQueue,
                buildFactory,
                abortPolicy);
        for (int i=1;i<=10;i++) {
            MyThread thread = new MyThread(i);
            threadPoolExecutor.execute(thread);
            System.out.println("线程池中现在的线程数目是:" + threadPoolExecutor.getPoolSize() + ",  队列中正在等待执行的任务数量为:" +
                    threadPoolExecutor.getQueue().size() + ",已经执行完的任务数量:" + threadPoolExecutor.getCompletedTaskCount());
        }
    }
package com.xiaoth.chengxiroad.robertcommon.thread;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.UUID;

/**
 * 创建线程
 * <p>
 * </p>
 *
 * @author libing
 * @since 2019-07-18 下午 5:12
 */
@Slf4j
@Data
public class MyThread implements Runnable,Comparable<MyThread> {

    private int priority;


    public MyThread() {

    }

    public MyThread(int priority) {
        this.priority = priority;
    }

    //当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高
    @Override
    public int compareTo(MyThread o) {
        log.info("this.priority:{},o.priority:{}",this.priority,o.priority);
        System.out.println();
        return  this.priority>o.priority?-1:1;
    }


    @Override
    public void run() {
        try {
            //让线程阻塞,使后续任务进入缓存队列
            Thread.sleep(1000);
            System.out.println("priority:"+this.priority+",ThreadName:"+Thread.currentThread().getName());
            String name = Thread.currentThread().getName();
            String sfasffas = name +"----"+ UUID.randomUUID();
            String substring = sfasffas.substring(0, 15);
            log.info("【{}】线程正在运行......",substring);
            log.info("优先级:{}",this.getPriority());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

 

    /**
     *
     *线程池的第四种任务队列:
     *
     */
    @Test
    public void priorityBlockingQueue (){
        threadPoolUtils.priorityBlockingQueue();
        while (true){

        }
    }

核心线程1,最大线程池2,

这个里面第一个会先进核心线程,然后所有的线程会到队列中去,然后由队列进行优先级的排序,优先级值越高先执行,和之前的队列不一样(先进先出)。在源码进行了排序

源码比较时调用了我们重写的compareTo方法

 

希望这篇文章对你能有所帮助……~……

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值