Java工作一年了,不会还不懂Java线程池的使用吧?(代码详解)

hello你好我是辰兮很高兴你能来阅读,本篇整理了线程池相关的代码案例,整理了好几天才实践完这篇文章,也希望自己对线程池有更深入的理解,欢迎阅读学习交流,分享获取新知,大家一起进步!


1、Preface序言

线程(英语:thread)是操作系统能够进行运算调度的最小单位。

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的降低频繁创建和销毁线程对性能所带来的开销。


Executors线程工厂类一共可以创建四种类型的线程池,通过Executors.newXXX即可创建。

ThreadPoolExecutor提供了四个构造函数

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

在Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,学习Java中的线程池,就是对ThreadPoolExecutor构造函数的参数的配置和学习。

int corePoolSize:该线程池中核心线程数最大值

int maximumPoolSize: 该线程池中线程总数最大值

long keepAliveTime:该线程池中非核心线程闲置超时时长

TimeUnit unit:keepAliveTime的单位

BlockingQueue workQueue:该线程池中的任务队列:维护着等待执行的Runnable对象

在这里插入图片描述
接下来用代码案例来真实展示常见线程池的创建过程以及如何运用,看一千次不如实践一次,try!!


2、FixedThreadPool

可重用固定线程数的线程池

newFixedThreadPool:创建一个定长的线程池,可控制线程的最大并发数,超出的线程在队列中等待。

    public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

在这里插入图片描述

有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。
在这里插入图片描述

核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。

/**
 * Created by 辰兮 2020/10/10
 * newFixedThreadPool 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
 */
public class NewFixedThreadPool {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++)
        {
    
    
            final int index = i;

            //1- 在未来某个时间执行给定的命令。
            // 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
            fixedThreadPool.execute(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    threadRunMethod(index);
                }
            });

            //2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            // 该 Future 的 get 方法在成功完成时将会返回给定的结果
            fixedThreadPool.submit(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    threadRunMethod(index);
                }
            });
        }
        fixedThreadPool.shutdown();
    }

    /**
     *
     * @param index
     */
    private static void threadRunMethod(int index) {
    
    
        try {
    
    
            System.out.println(index);
            Thread.sleep(2000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

0
0
1
1
2
2
3
3
4
4

Process finished with exit code 0

3、CachedThreadPool

可缓存的线程池(ps:速度快)

CachedThreadPool 缓存线程池,只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

    public static ExecutorService newCachedThreadPool() {
    
    
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

newCachedThreadPool:创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

/**
 * Created by 辰兮 2020/10/10
 * newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,
 * 若无可回收,则新建线程。线程池的规模不存在限制。
 */
public class NewCachedThreadPool {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
    
    
            final int index = i;
            try {
    
    
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            //1- 在未来某个时间执行给定的命令。
            // 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
            cachedThreadPool.execute(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    System.out.println(index);
                }
            });

            //2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            // 该 Future 的 get 方法在成功完成时将会返回给定的结果
            cachedThreadPool.submit(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    System.out.println(index);
                }
            });
        }
        cachedThreadPool.shutdown();
    }
}

0
0
1
1
2
2
3
3
4
4

Process finished with exit code 0

4、SingleThreadExecutor

单线程线程池(ps:速度相对较慢)

通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。干活效率慢。

public static ExecutorService newSingleThreadExecutor(){
    
    
    return new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

它只会创建一条工作线程处理任务;

采用的阻塞队列为LinkedBlockingQueue;

/**
 * Created by 辰兮 2020.10.10
 * newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
 * 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
 */
public class NewSingleThreadExecutor {
    
    
    public static void main(String[] args) {
    
    

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
    
    
            final int index = i;
            /*singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("newSingleThreadExecutor: " + index);
                        Thread.sleep(2*1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });*/

            singleThreadExecutor.submit(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    try {
    
    
                        System.out.println("newSingleThreadExecutor: " + index);
                        Thread.sleep(1*1000);
                    } catch (Exception e) {
    
    
                    	e.printStackTrace();
                    }
                }
            });
        }

        singleThreadExecutor.shutdown();
    }

}
newSingleThreadExecutor: 0
newSingleThreadExecutor: 1
newSingleThreadExecutor: 2
newSingleThreadExecutor: 3
newSingleThreadExecutor: 4

Process finished with exit code 0

5、ScheduledThreadPool

调度线程池

它用来处理延时任务或定时任务。

    /**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
    
    
        
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

这个线程池是唯一具有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。

/**
 * Created by 辰兮 2020/10/10
 * newScheduledThreadPool 创建一个固定长度线程池,支持定时及周期性任务执行。
 */
public class NewScheduledThreadPool {
    
    
    public static void main(String[] args) {
    
    
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

        //testSchedule(scheduledExecutorService);

        //testScheduleAtFixedRate(scheduledExecutorService);

        testScheduleWithFixedDelay(scheduledExecutorService);

        // 终止线程池
        //scheduledExecutorService.shutdown();
    }

    /**
     *
     * 跟 testScheduleAtFixedRate 非常类似,就是延迟的时间有点区别
     * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
     * 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
     * 接着在 initialDelay + 2 * period 后执行,依此类推。
     *
     * 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
     * 推迟的时间 : 等到上次的任务执行完之后再延迟period 的时间后执行。
     * @param scheduledExecutorService
     */
    private static void testScheduleWithFixedDelay(ScheduledExecutorService scheduledExecutorService) {
    
    
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    System.out.println("延迟2秒,再3秒执行一次");
                    //如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
                    //本次任务执行完后下次的任务还需要延迟period时间后再执行
                    Thread.sleep(6*1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        },2,3,TimeUnit.SECONDS);
    }

    /**
     * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
     * 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
     * 接着在 initialDelay + 2 * period 后执行,依此类推。
     *
     * 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
     * 推迟的时间 : 等到上次的任务执行完就立马执行。
     * @param scheduledExecutorService
     */
    private static void testScheduleAtFixedRate(ScheduledExecutorService scheduledExecutorService) {
    
    
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    System.out.println("延迟2秒,再3秒执行一次");
                    //如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
                    //如果任务里面执行的时间大于 period 的时间,本次任务执行完后,下次任务立马执行。
                    Thread.sleep(6*1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }

            }
        },2,3,TimeUnit.SECONDS);
    }

    /**
     * 创建并执行在给定延迟后启用的一次性操作
     * @param scheduledExecutorService
     */
    private static void testSchedule(ScheduledExecutorService scheduledExecutorService) {
    
    
        scheduledExecutorService.schedule(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("delay 3 seconds");
            }
        }, 3, TimeUnit.SECONDS);
    }
}
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
....

线程池的好处

1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

3、提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源。


阿里巴巴Java手册:为什么不建议使用Executors创建线程池?

了。
不建议采用Executors创建线程池,但是可以自定义线程池相关参数创建!


第一次整理关于线程池相关知识,如有不对欢迎大佬们指导!


The best investment is to invest in yourself.

在这里插入图片描述

2020.10.12 愿你们奔赴在自己的热爱里!

猜你喜欢

转载自blog.csdn.net/weixin_45393094/article/details/109037891