java 线程池的使用及原理(三):为什么要手动创建线程池

回顾 java.util.concurrent.Executors 提供的几种线程池,查看源码发现,除了jdk1.8 新增的newWorkStealingPool,其余的都是使用 ThreadPoolExecutor。所以,我们先来看看 ThreadPoolExecutor的神秘力量

ThreadPoolExecutor

  • 了解一个类,先看其构造函数,ThreadPoolExecutor 有以下四个构造函数
/* 构造函数1: 5个参数,调用 7 个参数的构造函数  */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
/* 构造函数2: 6个参数,调用 7 个参数的构造函数,比构造函数1多了ThreadFactory threadFactory  */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
/* 构造函数3:6个参数,调用 7 个参数的构造函数,比构造函数1 多了RejectedExecutionHandler handler  */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}
/* 构造函数4: 7个参数,真正干活的构造  */
/* 各个参数的作用
* param1:int corePoolSize ==> 线程池中核心的线程数,即一直工作的个数
* param2:int maximumPoolSize ==> 线程池中最大的线程数,即最忙碌时能开启的线程个数
* param3:long keepAliveTime ==> 空闲线程的存活时间,即不用的线程多久关闭
* param4:TimeUnit unit  ==> 与 param3 配合使用,制定时间单位
* param5:BlockingQueue<Runnable> workQueue ==> 存储任务的阻塞队列,当任务数 > 核心线程corePoolSize 时存储,注意,这里不会先开启线程,任务队列满了才会开启线程,后面证明  
* param6:ThreadFactory threadFactory ==> 线程工厂,负责创建线程工作
* param7:RejectedExecutionHandler handler  ==> 拒绝执行策略,即线程池满了,新任该怎么处理,defaultHandler = new AbortPolicy(); 	
*/
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

证明 param5 所说,超过核心线程数,先放进任务队列,任务队列满了,直接开启线程执行新添加的任务

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class BlockingTest {
    public static void main(String[] args) {
        /* 核心线程:2  最大线程:4  等待队列:2 */
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(2));
        for(int i = 1; i <= 4; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(3000);
                    System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是先添加的任务" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        for(int i = 1; i <= 2; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(3000);
                    System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是后添加的任务 -->" + finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        threadPool.shutdown();
    }
}
  • 可以看到,后添加的任务开启线程执行,先于 任务3、4。
    在这里插入图片描述

ThreadPoolExecutor 四种拒绝策略

  • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  • DiscardPolicy:丢弃任务,但是不抛出异常。
  • DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  • CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

1. AbortPolicy(默认)

  • AbortPolicy 类源码,可以看到,拒绝策略是 throw RejectedExecutionException()
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}
  • 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AbortPolicyTest {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 5,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(2));

        for(int i = 1; i <= 10; i++) {
            threadPool.execute(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " + Thread.currentThread().getName());
            });
        }

        Thread.sleep(1000);
        System.out.println("====================== 我是华丽的分割线 ====================");
        for(int i = 1; i <= 5; i++) {
            threadPool.execute(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " +  Thread.currentThread().getName());
            });
        }

    }
}
  • 抛出 RejectedExecutionException 错误,线程不停止,但不也继续往下执行?
    • 因为从报错信息也可以看出,错误是main线程抛出的, 又 ThreadPoolExecutor 的 allowCoreThreadTimeOut 默认为false,即核心线程一直保持运行,所以线程一直阻塞。在这里插入图片描述

2. DiscardPolicy

  • DiscardPolicy 类源码,rejectedExecution 为空,不做任何工作
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
  • 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
import java.util.concurrent.TimeUnit;

public class DiscardPolicyTest {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 5,
                0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(2),
                new DiscardPolicy());

        for(int i = 1; i <= 10; i++) {
            threadPool.execute(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " + Thread.currentThread().getName());
            });
        }
        // 确保之前的线程不受影响
        Thread.sleep(3000);
        System.out.println("====================== 我是华丽的分割线 ======================");
        for(int i = 1; i <= 10; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是 rejectedExecution后执行的线程: " +  Thread.currentThread().getName() + "\t 我是" + finalI);
            });
        }
        threadPool.shutdown();
    }
}

在这里插入图片描述

3. DiscardOldestPolicy

  • DiscardOldestPolicy 类源码,很明显可以看到,poll() 一个,在执行 execute()
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
  • 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.TimeUnit;

public class DiscardOldestPolicyTest {
    public static void main(String[] args) throws Exception {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5,
                0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(2),
                new DiscardOldestPolicy());

        for(int i = 1; i <= 20; i++) {
            int finalI = i;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            threadPool.execute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
            });
        }

        Thread.sleep(10000);

        System.out.println("====================== 我是华丽的分割线 ====================");
        for(int i = 1; i <= 20; i++) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " +  Thread.currentThread().getName() + "\t 我是" +  finalI);
            });
        }
        threadPool.shutdown();
    }
}

  • 可以看到,i = 19、20 输出了, 可以证明,最后的任务会挤兑前面的任务
    在这里插入图片描述

4. CallerRunsPolicy

  • CallerRunsPolicy 类源码,如果有阻塞,调用者直接执行任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
  • 测试代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.TimeUnit;

public class CallerRunsPolicyTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 5,
                0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(2),
                new CallerRunsPolicy());

        for(int i = 1; i <= 10; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " + Thread.currentThread().getName() + "\t 我是" + finalI);
            });
        }

        Thread.sleep(3000);

        System.out.println("====================== 我是华丽的分割线 ====================");
        for(int i = 1; i <= 10; i++) {
            int finalI = i;
            threadPool.execute(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("我是线程: " +  Thread.currentThread().getName() + "\t 我是" +  finalI);
            });
        }
        threadPool.shutdown();
    }
}

在这里插入图片描述

回看 java 提供的线程池

newSingleThreadExecutor 、newFixedThreadPool

  • 两者会提供一个无界的等待队列,当等待队列过多时,会报 OOM 异常
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • 代码示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class newSingleThreadExecutorTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        while(true) {
            executorService.execute(() -> {
                byte[] bytes = new byte[1024 * 1024 * 30];
                System.out.println("===============" + Thread.currentThread().getName());
            });
        }
        executorService.shutdown();
    }
}
  • 为了凸显内存异常,我设置了较小的堆内存参数 -Xms60m -Xmx60m
    在这里插入图片描述

newCachedThreadPool、newScheduledThreadPool

  • 两者会无限创建线程,报 OOM 异常
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}
  • 为了凸显内存异常,我设置了较小的堆内存参数 -Xms60m -Xmx60m
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NewCachedThreadPoolTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        while(true) {
            executorService.execute(() -> {
                byte[] bytes = new byte[1024 * 1024];
                try {
                    Thread.sleep(10000);
                    System.out.println("===============" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }
}

在这里插入图片描述

  • 除此之外,还可自定义等待队列,线程工厂,线程存活之间等。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值