线程池-自定义线程池

最近在研究Executors线程池,出了常用的4个基本创建线程池的方法,newFixedThreadPool(),newSingleThreadExextor(),newCachedThreadPool(),newScheduledThreadPool()之外,我们完全可以通过自定义的方式实现自己想要的线程池,进而满足我们项目需求。
对于自定义线程池也是基于ThreadPoolExecutor的构造函数来设置自定义的。先对其构造函数有一个基本了解

/**
     *
     * @param corePoolSize   池中所保存的核心线程数
     * @param maximumPoolSize  池中允许的最大线程数
     * @param keepAliveTime   非核心线程空闲等待新任务的最长时间,超过此时间,线程则会被回收
     * @param unit  参数时间单位
     * @param workQueue   任务队列
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                Executors.defaultThreadFactory(), defaultHandler);
    }

直接通过创建的方式就可以自定义一个我们想要的线程,自定义线程分为两种,种一:有界队列;种二:无界队列,今日我们通过有界队列对自定义线程有一个深入的了解。
何为有界队列?

  • 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列。若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,若线程数大于maximumPoolSize,则执行拒绝策略,或其他自定义方式。

通过创建一个简单的实体类和一个测试方法来实现。
实体类:

public class MyTask  implements Runnable{

    private int taskId;
    private String taskName;

    //构造函数
    public MyTask(int taskId,String taskName){
        this.taskId=taskId;
        this.taskName=taskName;
    }

    @Override
    public void run() {
        System.out.println("run taskId="+this.taskId);
        try {
            //每次调用线程都沉睡5秒
            Thread.sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public String toString(){
        return Integer.toString(this.taskId);
    }
}

测试类:我们通过创建ThreadPoolExecutor来控制线程数,指定想要的队列。

public static void main(String[] args) {

        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1,    //coreSize
                2,   //MaxSize
                60, //60
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(3)     //指定一种队列(有界队列)
        );

        MyTask mt1 = new MyTask(1, "任务1");
        MyTask mt2 = new MyTask(2, "任务2");
        MyTask mt3 = new MyTask(3, "任务3");
        MyTask mt4 = new MyTask(4, "任务4");
        MyTask mt5 = new MyTask(5, "任务5");
        MyTask mt6 = new MyTask(6, "任务6");

        pool.execute(mt1);
        pool.execute(mt2);
        pool.execute(mt3);
        pool.execute(mt4);
        pool.execute(mt5);
        pool.execute(mt6);
        pool.shutdown();
    }

从ThreadPoolExecutor创建的线程条件我们可以得知,我们创建了一个核心线程数,最大线程数为2,非核心线程等待的时间为60秒。

  • 只执行mt1,则符合核心线程数,所以会直接执行。因为设置的等待时间为60秒,意思就是假设执行mt1用了10秒,假如在剩下的50秒钟无任何线程进入,则该线程池直接被回收。若进入则直接执行下一个线程。
  • 执行mt1,mt2则是核心线程和最大线程,则会在执行完mt1之后,大概5秒之后,执行mt2;
  • ArrayBlockingQueue队列是可以容纳3个值的,所以为mt2,mt3,mt4都可以放入队列中,等待执行。执行4个线程中间的时间间隔则为设置的5秒左右。
  • 而mt5则已经超过了队列的容纳范围,所以重新创建一个新的线程,因为如需放入队列中,所以应该是和mt1同步的执行,无需等待。
  • 而mt6则直接拒绝。因为超过了,最大线程和队列存入个数。最后执行的效果如下:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task 6 rejected from java.util.concurrent.ThreadPoolExecutor@165e6c89[Running, pool size = 2, active threads = 2, queued tasks = 3, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
    at Thread.Executors.UseThreadPoolExecutor1.main(UseThreadPoolExecutor1.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
run taskId=1
run taskId=5
run taskId=2
run taskId=3
run taskId=4

通过错误数字都可以看到问题的所在。对于在项目中的使用情况,需要根绝具体需求而定。通过学习各种线程,线程池,有一个整体的规划,这样在使用时方能想到,熟能生巧。

转载于:https://www.cnblogs.com/huohuoL/p/10545447.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池自定义线程池工厂指的是我们可以通过自定义工厂类来创建线程池。在Java中,我们可以通过实现ThreadFactory接口来自定义线程池工厂。通过自定义工厂类,我们可以定制线程创建方式,例如给线程设置特定的命名规则、设置线程的优先级等。 自定义线程池工厂的步骤如下: 1. 创建一个实现ThreadFactory接口的自定义工厂类,并实现其`newThread(Runnable r)`方法。 2. 在`newThread`方法中,我们可以通过`Thread`类的构造方法来创建线程,并进行一些定制化的操作,比如设置线程的名称、优先级等。 3. 自定义线程池工厂类的实例化后,我们可以将其作为参数传递给线程池创建方法中,以便使用自定义线程池工厂来创建线程池。 举个例子,假设我们需要自定义线程池工厂来创建线程池,可以按照以下步骤进行: 1. 创建一个自定义线程池工厂类,例如`CustomThreadFactory`,并实现ThreadFactory接口。 2. 在`CustomThreadFactory`类中,实现`newThread(Runnable r)`方法,并在该方法中创建线程,并设置线程的名称。 3. 在使用线程池的地方,例如`Executors.newFixedThreadPool()`方法中,将`CustomThreadFactory`类的实例传递给`newFixedThreadPool()`方法,以使用自定义线程池工厂来创建线程池。 通过自定义线程池工厂,我们可以更加灵活地控制线程创建过程,并根据实际需求进行定制化操作。这样可以提高线程池的灵活性和可扩展性,使其更好地适用于各种场景的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值