二、JUC入门——线程创建(线程池详细说明)

    技术2025-11-12  4

    线程

    创建线程的几种方式1、继承Thread类2、实现Runnable接口3、实现Callable接口创建线程几种方式 4、线程池线程池详细解析四种拒绝策略:描述线程池分类,

    创建线程的几种方式

    在创建线程之前,我们要先熟悉一个类Thread;

    public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); } /*省略一堆*/ /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } } }

    重写的Runnable接口的run方法,为业务运行代码编写的地方,如果我们要实现业务书写,也需要重写run方法,再通过点start方法启动线程,等待CPU调用!

    1、继承Thread类

    /** * Auther: mayuhang <br/> * Date: 17:32:2020/6/29 <br/> * Description:方式1.继承方式创建,缺点是因为java是单继承,所以,无法再继承其他类; */ class ExtendsThread extends Thread{ @Override public void run() { System.out.println("继承Thread方式创建线程,重写run方法"); } //启动线程方法见下方 }

    2、实现Runnable接口

    /** * Auther: mayuhang <br/> * Date: 17:33:2020/6/29 <br/> * Description:方式2.实现runnable接口;接口可以多继承,可以实现更多的接口,完成更为复杂的业务 */ class RunnableImpl implements Runnable{ @Override public void run() { System.out.println("实现Runnable接口方式创建线程,重写run方法"); } //启动线程方法见下方 }

    3、实现Callable接口

    /** * Auther: mayuhang <br/> * Date: 17:34:2020/6/29 <br/> * Description:方式3,实现callable接口,有返回值,返回完成后的结果值!可以自定义返回值!也可以返回执行业务后的参数值! */ class CallableImp implements Callable<Object>{ @Override public Object call() throws Exception { System.out.println("实现Callable接口方式创建线程,重写call方法,有返回值"); return "输出Callable的返回值"; } //可以通过FutureTask类的其中一个构造方法使用Callable,另外一个构造方法可以使用Runnable public static void main(String[] args) throws Exception { CallableImp callableImp = new CallableImp(); FutureTask<Object> objectFutureTask = new FutureTask<>(callableImp); Thread thread1 = new Thread(objectFutureTask); thread1.start(); //打印返回值 System.out.println(objectFutureTask.get()); } }

    这里重点讲解线程池之前,简单过一下main中,使用线程的几种方式!

    创建线程几种方式

    public static void main(String[] args){ // 第一种创建方式:使用继承Thread的对象 ExtendsThread extendsThread = new ExtendsThread(); extendsThread.start(); // 第二种创建方式,使用runnable的实现类 RunnableImpl runnable = new RunnableImpl(); // 创建线程(使用有参构造函数) Thread thread = new Thread(runnable); // 启动线程,但是并不一定启动后立马执行哦 thread.start(); // 第三种,使用无参构造方法,重写Thread的run方法!:也可以直接使用用匿名内部类方式,具体其他创建方式还可查看Thread的其他构造函数(也可以使用匿名内部类) new Thread(){ @Override public void run() { //我们的业务方法 } }.start(); //匿名内部类(来自new Runnable接口!) new Thread(new Runnable() { @Override public void run() { //我们的run里面写的业务代码! } }).start(); //lamda表达式,等价于上面那种写法!! new Thread(()->{ //我们的run里面写的业务代码!具体lamda表达式,后面找个专题细讲一下! }).start();

    4、线程池

    // 第四种,线程池 // 【强制--来自阿里巴巴Java开发手册】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, // 这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险; // ExecutorService executorService = Executors.newSingleThreadExecutor(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,10,5000, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy()); for (int i = 0; i <10 ; i++) { //有返回值方式执行业务业务方法,参考第三种线程创建方式Callable;来自接口ExecutorService的submit(Runnable task)执行 Future<Object> submit = threadPoolExecutor.submit(new Callable<Object>() { @Override public Object call() throws Exception { System.out.println("submit运行线程"+Thread.currentThread().getName()); return "线程池的submit(Callable)"; } }); System.out.println("------------------"+submit.get()); //线程池无返回值方式执行业务方法,调用父类线程池Executor中的execute(Runnable command)实现;(new Runnable为匿名内部类!) threadPoolExecutor.execute(new Runnable() { @Override public void run() { System.out.println("execute运行线程"+Thread.currentThread().getName()); } }); }

    线程池详细解析

    首先我们来看看线程池的构造函数:

    // Public constructors and methods /** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters and default thread factory and rejected execution handler. * It may be more convenient to use one of the {@link Executors} factory * methods instead of this general purpose constructor. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @throws IllegalArgumentException if one of the following holds: * {@code corePoolSize < 0} * {@code keepAliveTime < 0} * {@code maximumPoolSize <= 0} * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }

    我们通过这个图,来理解里面的几个参数: 第一个参数:int corePoolSize 核心线程,上图绿色方框中的内容! 第二个参数:int maximumPoolSize 最大线程,核心线程和非核心线程的总数! 第三个参数:long keepAliveTime 存活时间,非核心线程的存活时间! 第四个参数:TimeUnit unit 存活时间单位,来自JUC中的TimeUnit枚举类! 第五个参数:BlockingQueue workQueue 阻塞队列 可以new 它的实现类,作为这个参数,配置上对应的长度即可 比如:new ArrayBlockingQueue(5) 长度五的阻塞队列! 第六个参数:RejectedExecutionHandler handler 异常处理方式,为红色部分所示!具体使用方式如下图所示,使用例子:new ThreadPoolExecutor.DiscardOldestPolicy()“丢弃老任务(队头)策略”

    package com.woniuxy.concurrent.executor; import java.util.concurrent.*; /** * Auther: mayuhang <br/> * Date: 2020/6/15:9:36 <br/> * Description:线程池创建 */ public class ExecutorPoolTest1 { static int j = 0; public static void main(String[] args) { /** * Auther: mayuhang <br/> * Date: 15:19:2020/7/6 <br/> * Description: 表示核心线程5个,总线程数量10个,非核心线程5个存活时间为5000微秒, * 使用 new ArrayBlockingQueue<Runnable>(5)队列,队列容量5,可以放入5个待执行任务, * 异常处理方式为new ThreadPoolExecutor.AbortPolicy() 抛出异常! */ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy()); for (int i = 1; i <= 16; i++) { threadPoolExecutor.submit(new Runnable() { @Override public void run() { System.out.println("我是"+Thread.currentThread().getName()+"线程,正在执行" + (++ExecutorPoolTest1.j) + "的任务!"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } },i); } } } 打印结果: 我是pool-1-thread-1线程,正在执行1的任务! 我是pool-1-thread-4线程,正在执行4的任务! 我是pool-1-thread-2线程,正在执行2的任务! 我是pool-1-thread-3线程,正在执行3的任务! 我是pool-1-thread-5线程,正在执行5的任务! 我是pool-1-thread-6线程,正在执行6的任务! 我是pool-1-thread-7线程,正在执行7的任务! 我是pool-1-thread-8线程,正在执行8的任务! 我是pool-1-thread-9线程,正在执行9的任务! 我是pool-1-thread-10线程,正在执行10的任务! Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5cad8086 rejected from java.util.concurrent.ThreadPoolExecutor@6e0be858[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369) at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123) at com.woniuxy.concurrent.executor.ExecutorPoolTest1.main(ExecutorPoolTest1.java:25) 我是pool-1-thread-7线程,正在执行11的任务! 我是pool-1-thread-4线程,正在执行13的任务! 我是pool-1-thread-10线程,正在执行12的任务! 我是pool-1-thread-5线程,正在执行14的任务! 我是pool-1-thread-8线程,正在执行15的任务!

    通过上面这个方法,我们可以看出,如果任务数量正好是15(for循环次数)个,则线程池中可以创建的总线程数量为10个(new ThreadPoolExecutor的第二个参数), 加上5个阻塞队列(new ArrayBlockingQueue<>(5)),这个线程池只可以同时执行10个任务,然后还有5个任务在阻塞队列中! 而在这个例子里,我们创建了16个任务(for循环次数),在任务中,使用了sleep来延长任务存在的时间,则16个任务,超过了,10个线程加5个阻塞队列的容量,于是抛出了异常(拒绝策略使用的是默认的)! 如果切换最后一个参数(new ThreadPoolExecutor.AbortPolicy()),换掉拒绝策略,会有什么区别呢??请参考下方,自行尝试!

    四种拒绝策略:

    ThreadPoolExecutor.AbortPolic(默认):当任务添加到线程池之中被拒绝的时候,这个时候会抛出RejectedExecutionException异常ThereeadPoolExecutor.CallerRunsPolicy:当任务被拒绝的时候,会在线程池当前正在执行线程的WORKER里面处此线程ThreadPoolExecutor.DiscardOldestPoliy:当被拒绝的时候,线程池会放弃队列之中等待最长时间的任务,并且将被拒绝的任务添加到队列之中ThreadPoolExecutor.DiscardPolicy:当任务添加拒绝的时候,将直接丢弃此线程

    描述线程池分类,

    分类:除了上述这种示例,一下4中也是比较常用的!

    创建无大小限制的线程池(newCachedThreadPool())创建固定大小的线程池(newFixedThreadPool(int nThreads))单线程池 (newSingleThreadScheduledExecutor())创建定时调度池 (newScheduledThreadPool(int corePoolSize)
    Processed: 0.014, SQL: 9