温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Java线程池的使用方法有哪些

发布时间:2023-03-24 14:29:33 来源:亿速云 阅读:119 作者:iii 栏目:开发技术

本文小编为大家详细介绍“Java线程池的使用方法有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java线程池的使用方法有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

    线程池的创建⽅法总共有 7 种,但总体来说可分为 2 类: 

    1. 通过 ThreadPoolExecutor 创建的线程池;        

    2. 通过 Executors 创建的线程池。

    线程池的创建⽅式总共包含以下 7 种(其中 6 种是通过 Executors 创建的, 1 种是通过 ThreadPoolExecutor 创建的):

    1. Executors.newFixedThreadPool:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待;         
    2. Executors.newCachedThreadPool:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程;        
     3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序;         
    4. Executors.newScheduledThreadPool:创建⼀个可以执⾏延迟任务的线程池;         
    5. Executors.newSingleThreadScheduledExecutor:创建⼀个单线程的可以执⾏延迟任务的线程池;
    6. Executors.newWorkStealingPool:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。
     7. ThreadPoolExecutor:最原始的创建线程池的⽅式,它包含了 7 个参数可供设置,后⾯会详细讲。

     1. 固定数量的线程池

    public class ThreadPoolDemo3 {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newFixedThreadPool(2);
            //添加任务方式 1
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
     
            //添加任务方式2
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
    输出:
    pool-1-thread-1
    pool-1-thread-2

    a.  线程池返回结果

    public class ThreadPoolDemo4 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ExecutorService threadPool =  Executors.newFixedThreadPool(2);
            //执行任务
            Future<Integer> result = threadPool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(10);
                    System.out.println("随机数" + num);
                    return num;
                }
            });
     
            //打印线程池返回方式
            System.out.println("返回结果:" + result.get());
        }
    }
    输出
    随机数8
    返回结果:8

    使用submit可以执行有返回值的任务或者是无返回值的任务;而execute只能执行不带返回值的任务。 

    Java线程池的使用方法有哪些

    b. ⾃定义线程池名称或优先级

    public class ThreadPoolDemo5 {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
             // 创建线程工厂
            ThreadFactory threadFactory = new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    //!!!!!!!一定要注意:要把任务Runnable设置给新创建的线程
                    Thread thread = new Thread(r);
                    //设置线程的命名规则
                    thread.setName("我的线程" + r.hashCode());
                    //设置线程的优先级
                    thread.setPriority(Thread.MAX_PRIORITY);
                    return thread;
                }
            };
            ExecutorService threadPool = Executors.newFixedThreadPool(2,threadFactory);
            //执行任务1
            Future<Integer> result = threadPool.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    int num = new Random().nextInt(10);
                    System.out.println(Thread.currentThread().getPriority() + ", 随机数:" + num);
                    return num;
                }
            });
            //打印线程池返回结果
            System.out.println("返回结果:" + result.get());
        }
    }

     提供的功能:

            1. 设置(线程池中)线程的命名规则。

            2. 设置线程的优先级。

            3. 设置线程分组。

            4. 设置线程类型(用户线程、守护线程)。

    2. 带缓存的线程池

    public class ThreadPoolDemo6 {
        public static void main(String[] args) {
            //创建线程池
            ExecutorService service = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {
                int finalI = i;
                service.submit(() -> {
                    System.out.println("i : " + finalI + "|线程名称:" + Thread.currentThread().getName());
                });
            }
        }
    }
    输出
    i : 1|线程名称:pool-1-thread-2
    i : 4|线程名称:pool-1-thread-5
    i : 3|线程名称:pool-1-thread-4
    i : 5|线程名称:pool-1-thread-6
    i : 0|线程名称:pool-1-thread-1
    i : 2|线程名称:pool-1-thread-3
    i : 6|线程名称:pool-1-thread-7
    i : 7|线程名称:pool-1-thread-8
    i : 8|线程名称:pool-1-thread-9
    i : 9|线程名称:pool-1-thread-1

    优点:线程池会根据任务数量创建线程池,并且在一定时间内可以重复使用这些线程,产生相应的线程池。

    缺点:适用于短时间有大量任务的场景,它的缺点是可能会占用很多的资源。

    3. 执⾏定时任务 a. 延迟执⾏(⼀次)

    public class ThreadPoolDemo7 {
        public static void main(String[] args) {
            //创建线程池
            ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
            System.out.println("添加任务的时间:" + LocalDateTime.now());
            //执行定时任务(延迟3s执行)只执行一次
            service.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行子任务:" + LocalDateTime.now());
                }
            },3, TimeUnit.SECONDS);
        }
    }
    输出
    添加任务的时间:2022-04-13T14:19:39.983
    执行子任务:2022-04-13T14:19:42.987

    Java线程池的使用方法有哪些

      b. 固定频率执⾏

    public class ThreadPoolDemo8 {
        public static void main(String[] args) {
            //创建线程池
            ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
            System.out.println("添加任务时间:" + LocalDateTime.now());
            //2s之后开始执行定时任务,定时任务每隔4s执行一次
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务:" + LocalDateTime.now());
                }
            },2,4, TimeUnit.SECONDS);
        }
    }
    输出
    添加任务时间:2022-04-13T14:24:38.810
    执行任务:2022-04-13T14:24:40.814
    执行任务:2022-04-13T14:24:44.814
    执行任务:2022-04-13T14:24:48.813
    执行任务:2022-04-13T14:24:52.815
    执行任务:2022-04-13T14:24:56.813
    执行任务:2022-04-13T14:25:00.813
    执行任务:2022-04-13T14:25:04.814
    执行任务:2022-04-13T14:25:08.813
    ... ...
    ... ...
    执行任务:2022-04-13T14:26:44.814
    执行任务:2022-04-13T14:26:48.813

    Java线程池的使用方法有哪些

     注意事项:

    public class ThreadPoolDemo9 {
        public static void main(String[] args) {
            //创建线程池
            ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
            System.out.println("添加任务时间:" + LocalDateTime.now());
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务: " + LocalDateTime.now());
                    try {
                        Thread.sleep(5 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },2,4, TimeUnit.SECONDS);
        }
    }
    输出
    添加任务时间:2022-04-13T14:33:34.551
    执行任务: 2022-04-13T14:33:36.556
    执行任务: 2022-04-13T14:33:41.557
    执行任务: 2022-04-13T14:33:46.559
    执行任务: 2022-04-13T14:33:51.561
    执行任务: 2022-04-13T14:33:56.562
    执行任务: 2022-04-13T14:34:01.564
    执行任务: 2022-04-13T14:34:06.566
    执行任务: 2022-04-13T14:34:11.566
    执行任务: 2022-04-13T14:34:16.567
    执行任务: 2022-04-13T14:34:21.570
    执行任务: 2022-04-13T14:34:26.570
    ... ....

    Java线程池的使用方法有哪些

    c. scheduleAtFixedRate VS scheduleWithFixedDelay

    scheduleAtFixedRate 是以上⼀次任务的开始时间,作为下次定时任务的参考时间的(参考时间+延迟任务=任务执⾏)。 scheduleWithFixedDelay 是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。

    public class ThreadPoolDemo10 {
        public static void main(String[] args) {
            //创建线程池
            ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
            System.out.println("添加任务时间:" + LocalDateTime.now());
            //2s之后开始执行定时任务,定时任务每隔4s执行一次
            service.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行任务:" + LocalDateTime.now());
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, 2, 4, TimeUnit.SECONDS);
        }
    }
    输出
    添加任务时间:2022-04-13T14:46:02.871
    执行任务:2022-04-13T14:46:04.876
    执行任务:2022-04-13T14:46:09.878
    执行任务:2022-04-13T14:46:14.880
    执行任务:2022-04-13T14:46:19.883
    执行任务:2022-04-13T14:46:24.885
    执行任务:2022-04-13T14:46:29.888
    执行任务:2022-04-13T14:46:34.888
    执行任务:2022-04-13T14:46:39.891
    执行任务:2022-04-13T14:46:44.893
    执行任务:2022-04-13T14:46:49.895
    执行任务:2022-04-13T14:46:54.897
    执行任务:2022-04-13T14:46:59.900
    执行任务:2022-04-13T14:47:04.901
    ... ...

    Java线程池的使用方法有哪些

    4. 定时任务单线程

    public class ThreadPoolDemo11 {
        public static void main(String[] args) {
            ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
            System.out.println("添加任务的时间:" + LocalDateTime.now());
            service.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("执行时间:" + LocalDateTime.now());
                }
            },2, TimeUnit.SECONDS );
        }
    }
    输出
    添加任务的时间:2022-04-13T15:06:38.100
    执行时间:2022-04-13T15:06:40.106

    5. 单线程线程池

    public class ThreadPoolDemo12 {
        public static void main(String[] args) {
            ExecutorService service = Executors.newSingleThreadScheduledExecutor();
            for (int i = 0; i < 10; i++) {
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("线程名:" + Thread.currentThread().getName());
                    }
                });
            }
        }
    }
    输出
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1
    线程名:pool-1-thread-1

    (MS) 为什么不直接用线程?

    单线程的线程池又什么意义?

            1. 复用线程。

            2. 单线程的线程池提供了任务队列和拒绝策略(当任务队列满了之后(Integer.MAX_VALUE),新来的任务就会拒绝策略)

    6. 根据当前CPU⽣成线程池

    public class ThreadPoolDemo13 {
        public static void main(String[] args) {
            ExecutorService service = Executors.newWorkStealingPool();
            for (int i = 0; i < 10; i++) {
                service.submit(() -> {
                    System.out.println("线程名" + Thread.currentThread().getName());
                });
                
                while(!service.isTerminated()) {
                }
            }
        }
    }
    输出
    线程名ForkJoinPool-1-worker-1

    7. ThreadPoolExecutor

    (1). Executors ⾃动创建线程池可能存在的问题

    Java线程池的使用方法有哪些

    a. OOM 代码演示
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
     
    public class ThreadPoolDemo54 {
        static class MyOOMClass {
            // 1M 空间(M KB Byte)
            private byte[] bytes = new byte[1 * 1024 * 1024];
       }
        public static void main(String[] args) throws InterruptedException {
            Thread.sleep(15 * 1000);
            ExecutorService service = Executors.newCachedThreadPool();
            Object[] objects = new Object[15];
            for (int i = 0; i < 15; i++) {
                final int finalI = i;
                service.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(finalI * 200);
                       } catch (InterruptedException e) {
                            e.printStackTrace();
                       }
                        MyOOMClass myOOMClass = new MyOOMClass();
                        objects[finalI] = myOOMClass;
                        System.out.println("任务:" + finalI);
                   }
               });
           }
       }
    }

    Java线程池的使用方法有哪些

    Java线程池的使用方法有哪些

     执行结果:

    Java线程池的使用方法有哪些

    b. 关于参数设置

    -XX:标准设置,所有 HotSpot 都⽀持的参数。 -X:⾮标准设置,特定的 HotSpot 才⽀持的参数。 -D:程序参数设置,-D参数=value,程序中使⽤:System.getProperty("获取")。

    mx 是 memory max 的简称 

    (2).  ThreadPoolExecutor 使⽤

    a. ThreadPoolExecutor 参数说明

    Java线程池的使用方法有哪些

    b. 线程池执⾏流程

    Java线程池的使用方法有哪些

    c. 执⾏流程验证

     <MS>

    线程池的重要执行节点:

    1. 当任务来了之后,判断线程池中实际线程数是否小于核心线程数,如果小于就直接创建并执行任务。

    2. 当实际线程数大于核心线程数(正式员工),他就会判断任务队列是否已满,如果未满就将任务存放队列即可。

    3. 判断线程池的实际线程数是否大于最大线程数(正式员工 + 临时员工),如果小于最大线程数,直接创建线程执行任务;实际线程数已经等于最大线程数,则会直接执行拒绝策略。

    d. 拒绝策略

    (4种 JDK 提供的拒绝策略 + 1 种 自定义拒绝策略)

    Java线程池的使用方法有哪些

    Java ⾃带的拒绝策略,CallerRunsPolicy:

    public class ThreadPoolDemo14 {
        public static void main(String[] args) {
            ThreadFactory factory = new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    Thread thread = new Thread(r);
                    return thread;
                }
            };
            // 手动创建线程池
            ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1), new ThreadPoolExecutor.CallerRunsPolicy());
            for (int i = 0; i < 4; i++) {
                int finalI = i;
                executor.submit(() -> {
                    System.out.println(Thread.currentThread().getName() + "执行任务:" + finalI);
                });
            }
            //终止线程
            executor.shutdown();
        }
    }
    输出
    main执行任务:2
    pool-1-thread-1执行任务:0
    main执行任务:3
    pool-1-thread-1执行任务:1

    ⾃定义拒绝策略:

    Java线程池的使用方法有哪些

    线程状态

    Java线程池的使用方法有哪些

    shutdown 执⾏时线程池终⽌接收新任务,并且会将任务队列中的任务处理完; shutdownNow 执⾏时线程池终⽌接收新任务,并且会给终⽌执⾏任务队列中的任务。

    1. RUNNING:这个没什么好说的,这是最正常的状态:接受新的任务,处理等待队列中的任务; SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务; 2. STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执⾏任务的线程;

    3. TIDYING:所有的任务都销毁了,workCount 为 0。线程池的状态在转换为 TIDYING 状态时,会执 ⾏钩⼦⽅法 terminated();

    4. TERMINATED:terminated() ⽅法结束后,线程池的状态就会变成这个。

    读到这里,这篇“Java线程池的使用方法有哪些”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI