Java并发多线程与高并发(六)-线程池

本文最后更新于:2024年4月22日 下午

针对性的学习线程池框架 ExecutorService ,包括核心概念,核心参数,创建使用等

创建线程的方式

继承Thread

  • 定义类继承Thread,重写run方法(线程执行的内容),又叫执行体
  • 创建Thread继承类的实例,调用start()启动线程
package cn.hyqup.concurrency.thread;

import lombok.extern.slf4j.Slf4j;

/**
 * Copyright © 2021灼华. All rights reserved.
 *
 * @author create by hyq
 * @version 1.0
 * @date 2021/5/16
 * @description:
 */
@Slf4j
public class ThreadExample1 extends Thread {
    /**
     * 重写run方法
     */
    @Override
    public void run() {
        log.info("继承thread开启线程");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadExample1().start();
        }
    }
}

实现Runable

  • 定义类实现runable,重写run方法(线程执行的内容),又叫执行体

  • 创建Runnable实现类的实例, new Thread作为入参传递。得到一个线程对象

  • 调用start()启动线程

    package cn.hyqup.concurrency.thread;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * Copyright © 2021灼华. All rights reserved.
     *
     * @author create by hyq
     * @version 1.0
     * @date 2021/5/16
     * @description:
     */
    @Slf4j
    public class ThreadExample2 implements Runnable {
        /**
         * 重写run方法
         */
        @Override
        public void run() {
            log.info("实现Runnable开启线程");
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                ThreadExample2 threadExample2 = new ThreadExample2();
                new Thread(threadExample2).start();
            }
        }
    }
    

实现 Callable ,并结合 Future

  • 定义Callable接口的实现类,实现call()方法,又叫执行体,并且定义返回值类型
  • 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,并且FutureTask返回类型和Callable的返回类型一致
  • new Thread,FutureTask做入参得到一个Thread线程对象
  • 调用Thread的start()方法,执行开启线程
  • 调用FutureTask对象的get()方法,得到子线程返回值
package cn.hyqup.concurrency.thread;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * Copyright © 2021灼华. All rights reserved.
 *
 * @author create by hyq
 * @version 1.0
 * @date 2021/5/16
 * @description:
 */
@Slf4j
public class ThreadExample3 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        return 100;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadExample3 threadExample3 =new ThreadExample3();
        FutureTask<Integer> futureTask = new FutureTask<>(threadExample3);
        for (int i = 0; i < 5; i++) {
            new Thread(futureTask).start();
            Integer integer = futureTask.get();
            log.info("子线程返回的值"+integer);
        }


    }


}

使用线程池创建线程(这里介绍JDK自带的Executors )

除了上述三种创建线程的方式,我们还可以通过线程池来创建线程

new Thread 的弊端

  • 每次都new对象,性能差,复用性差
  • 缺乏线程之间统一管理,造成资源浪费甚至可能内存溢出
  • 缺乏更多功能,如定时执行、定期执行、线程中断。

线程池的好处

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、定期执行、单线程、并发数控制等功能。

线程池核心概念及参数介绍

public ThreadPoolExecutor(int corePoolSize,
                            int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory,
                            RejectedExecutionHandler handler) 
  • corePoolSize(核心线程数)
  • maximumPoolSize(最大线程数)
  • keepAliveTime(线程活动保持存活时间)
  • unit(时间单位)
  • workQueue(任务存储队列)
    • SynchronousQueue(直接交接):没有工作队列缓冲区,每次直接扔到线程去处理,不能处理则抛异常
    • LinkedBlockingQueue(无界队列):队列永远不会满,也就是永远用不上最大线程数
    • ArrayBlockingQueue(有界队列):基于数组结构的有界阻塞队列,遵循先进先出
  • ThreadFactory(创建线程的工厂)
  • handler(拒绝策略)
    • DiscardPolicy:不处理,丢弃掉。
    • AbortPolicy:直接抛出异常。
    • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
    • CallerRunsPolicy:只用调用者所在线程来运行任务。

增减线程的特点

自动创建线程池的风险分析

针对线程池默认的创建线程池的构造方法的分析

  • newFixedThreadPool

    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待队列用的是LinkedBlockingQueue(新任务一直向队列中放)

    缺点:容易造成大量内存占用,可能会导致OOM

  • newSingleThreadExector

    创建一个定长为1的线程池。类似于newFixedThreadPool,队列用的是LinkedBlockingQueue(新任务一直向队列中放)

    缺点:容易造成大量内存占用,可能会导致OOM

  • newCachedThreadPool

    创建一个可缓存线程池,核心数量为0,最大线程数为整数最大值

    缺点:新来任务直接创建线程执行,也可能造成OOM

  • newScheduledThreadPool

    建一个定长线程池,支持定时及周期性任务执行,可以延迟,可以一定频率

总结:正确的线程池的创建应该是手动创建线程池,依据是调研后,根据业务定制


Java并发多线程与高并发(六)-线程池
https://hyq965672903.gitee.io/posts/2e7e1308.html
作者
灼华
发布于
2021年4月28日
许可协议