回顾多线程的创建、线程池的工作流程等

前言

近期对项目进行了回顾总结,来分享下我在项目中如何使用多线程来实现异步任务的,在分享之前,我们先来回顾下多线程的相关知识点

线程的四种创建方式

  • 继承Thread类,重写run方法
  • 实现Runnable接口,重写run方法
  • 实现Callable接口,配合FutureTask来实现返回值,重写call方法
  • 创建一个线程池,通过线程池调度
    我们来看下代码:
import java.util.concurrent.*;
public class ThreadTest {
    
    

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        // System.out.println("main......start.....");

        // 1、通过继承Thread类
        // Thread thread = new Thread01();
        // thread.start();
        // System.out.println("main......end.....");

		// 2、通过实现Runnable接口
        // Runable01 runable01 = new Runable01();
        // new Thread(runable01).start();
		
		// 3、通过FutureTask传参Callable,实现有返回值
        // FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        // new Thread(futureTask).start();
        // System.out.println(futureTask.get());

        // service.execute(new Runable01());

        System.out.println("main......start.....");
    }

    private static void threadPool() {
    
    

        ExecutorService threadPool = new ThreadPoolExecutor(
                200,
                10,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }


    public static class Thread01 extends Thread {
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }


    public static class Runable01 implements Runnable {
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
        }
    }


    public static class Callable01 implements Callable<Integer> {
    
    
        @Override
        public Integer call() throws Exception {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果:" + i);
            return i;
        }
    }
}

四种方式下创建线程启动的区别

第一种和第二种都是无返回值的创建线程执行任务,第三种是有返回值的创建线程执行任务。
以上这三种创建线程的方式在业务中是不能出现的,因为它们的创建线程和销毁线程是非常消耗时间的,影响业务的响应时间,抛开这些不讲,它也非常的消耗资源,假设我现在有10万的并发请求打过来,如果我用以上三种的其中一种创建线程执行任务的话,理想状态下是需要创建10万个线程,这是不可能的,在期间肯定会造成内存爆了,服务器宕机的。再这样的情况下我们可以用线程池来解决这个问题
我们来归拢一下在开发中为什么使用线程池的原因

  • 降低资源的消耗:重复利用已经创建好的线程来降低线程的创建和销毁带来的损耗
  • 提高响应的速度:线程池中有的线程处于空闲状态,当任务来的时候,就无需创建线程就可以立即去执行
  • 线程池会根据当前系统的特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销,无限的创建线程不仅销毁系统资源,还会降低系统的稳定性,都交给线程池来统一管理了。

线程池的相关知识点

怎么样创建一个线程池呢?
可通过这段代码来执行

ExecutorService threadPool = new ThreadPoolExecutor(
    200,
    10,
    10L,
    TimeUnit.SECONDS,
    new LinkedBlockingDeque<Runnable>(10000),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

我们来看下构造器中的参数(深入源码)

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @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.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    
    

corePoolSize:核心线程数量
maximumPoolSize:最大线程数量
keepAliveTime:线程池中超过核心线程数量在没有任务执行时最大生存时间,超过这个时间该线程就会被销毁
unit:时间单位
workQueue:工作队列,当任务数量超过核心线程的数量是,就会将这些任务放到工作对列中去,只要有空闲线程,这些空闲线程就会去队列中去取
threadFactory:创建线程的工厂
handler:当线程池不能再接受新任务时,采取的拒绝策略

线程池的工作流程
当创建一个线程池后,当提交的任务小于核心线程的数量时,核心线程就会立即去执行任务,当数量超过了核心线程数量的时候,就会将这些任务放到工作队列中去,当工作队列满了的话,就会创建非核心线程去执行任务,当线程池中的线程数量达到最大线程数量的时候,如果此时再提交任务,就会触发拒绝策略拒绝任务,当线程池中的任务都执行完成之后,就会有线程处于空闲状态,当处于空闲时间达到keepalive时间后,那些非核心线程就会被销毁,核心线程默认不会被销毁
jdk提供好的线程池

// 固定线程数量的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
// 核心线程为0,但可以任意创建线程,最后都能被销毁
public static ExecutorService newCachedThreadPool() {
    
    
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
// 单个线程的线程池
public static ExecutorService newSingleThreadExecutor() {
    
    
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
// 执行定时任务的线程池,它可以指定多长时间以后执行任务
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    
    
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

拒绝策略
在这里插入图片描述
第一种拒绝策略是:当线程池不能再接受任务的时候,可以去调用将要提交任务的run方法,只不过是同步的方式了,不是异步执行了
第二种拒绝策略是:当线程池不能再接受任务的时候,当又要有任务提交时,直接拒绝执行任务,并抛出异常
第三种拒绝策略是:当线程池不能再接受任务的时候,当有任务提交时,将这个任务删除,但不抛出异常
第四种拒绝策略是:当线程池不能再接受任务的时候,当有任务提交时,将队列中生存时间最长的任务删除,将当前任务加入到工作队列中去

猜你喜欢

转载自blog.csdn.net/MarkusZhang/article/details/107871967