Executors创建线程池有哪几种方式

参考答案

1.  关于Executors类

  • Executors 类是从 JDK 1.5 开始就新增的线程池创建的静态工厂类,用来创建线程池的。
  • 但是,现在很多大厂已经不建议使用该类去创建线程池。原因在于,该类创建的很多线程池的内部使用了无界任务队列,在并发量很大的情况下会导致 JVM 抛出 OutOfMemoryError,直接让 JVM 崩溃,影响严重。

2.  Executors 类的使用方法

2.1   newFixedThreadPool

创建定长线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestNewFixedThreadPool {

    public static void main(String[] args) {
        //创建工作线程数为 3 的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程数量不再变化,当线程发生错误结束时,线程池会补充一个新的线程
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
        //提交 6 个任务
        for (int i = 0; i <6; i++) {
            final int index = i;
            fixedThreadPool.execute(() -> {
                try {
                    //休眠 3 秒
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " index:" + index);
            });
        }
        
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4秒后...");
        
        //关闭线程池后,已提交的任务仍然会执行完
        fixedThreadPool.shutdown();
    }
    
}

结果:

pool-1-thread-2 index:1
pool-1-thread-3 index:2
pool-1-thread-1 index:0
4秒后...
pool-1-thread-1 index:4
pool-1-thread-3 index:5
pool-1-thread-2 index:3

2.2   newCachedThreadPool

创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestNewCachedThreadPool {
    
    public static void main(String[] args) {
        //创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,线程池的容量不限制
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        for (int i = 0; i <6; i++) {
            final int index = i;
            cachedThreadPool.execute(() -> {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " index:" + index);
            });
        }
        
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4秒后...");
        
        cachedThreadPool.shutdown();
        
    }
    
}

结果:创建的线程数与任务数相等。

pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-6 index:5
pool-1-thread-4 index:3
pool-1-thread-5 index:4
pool-1-thread-2 index:1
4秒后...

2.3   newScheduledThreadPool

创建定长线程池,可执行周期性的任务。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TestNewScheduledThreadPool {

    public static void main(String[] args) {
        //创建定长线程池,可执行周期性的任务
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        
        for (int i = 0; i <3; i++) {
            final int index = i;
            //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
            scheduledThreadPool.scheduleWithFixedDelay(() -> {
                    System.out.println(Thread.currentThread().getName() + " index:" + index);
            }, 0, 3, TimeUnit.SECONDS);
        }
        
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4秒后...");
        
        scheduledThreadPool.shutdown();

    }
}

结果:

pool-1-thread-1 index:0
pool-1-thread-3 index:2
pool-1-thread-2 index:1
pool-1-thread-1 index:0
pool-1-thread-2 index:1
pool-1-thread-3 index:2
4秒后...

2.4  newSingleThreadExecutor

创建单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestNewSingleThreadExecutor {
    
    public static void main(String[] args) {
        //单线程的线程池,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
        
        //提交 3 个任务
        for (int i = 0; i <3; i++) {
            final int index = i;
            singleThreadPool.execute(() -> {
                
                //执行第二个任务时,报错,测试线程池会创建新的线程执行任务三
                if (index == 1) {
                    throw new RuntimeException("线程执行出现异常");
                }
                
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " index:" + index);
            });
        }
        
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4秒后...");
        
        singleThreadPool.shutdown();
    }

}

结果:即使任务出现了异常,线程池还是会自动补充一个线程继续执行下面的任务。

pool-1-thread-1 index:0
Exception in thread "pool-1-thread-1" 
java.lang.RuntimeException: 线程执行出现异常
    at constxiong.concurrency.a011.TestNewSingleThreadExecutor.lambda$0(TestNewSingleThreadExecutor.java:21)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
4秒后...
pool-1-thread-2 index:2

2.5   newSingleThreadScheduledExecutor

创建单线程可执行周期性任务的线程池。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TestNewSingleThreadScheduledExecutor {

    public static void main(String[] args) {
        //创建单线程可执行周期性任务的线程池
        ScheduledExecutorService singleScheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
        
        //提交 3 个固定频率执行的任务
        for (int i = 0; i <3; i++) {
            final int index = i;
            //scheduleWithFixedDelay 固定的延迟时间执行任务; scheduleAtFixedRate 固定的频率执行任务
            singleScheduledThreadPool.scheduleAtFixedRate(() -> {
                System.out.println(Thread.currentThread().getName() + " index:" + index);
            }, 0, 3, TimeUnit.SECONDS);
        }
        
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4秒后...");
        
        singleScheduledThreadPool.shutdown();
    }
    
}

结果:0-2 任务都被执行了 2 个周期。

pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
pool-1-thread-1 index:0
pool-1-thread-1 index:1
pool-1-thread-1 index:2
4秒后...

2.6  newWorkStealingPool

创建任务可窃取线程池,空闲线程可以窃取其他任务队列的任务,不保证执行顺序,适合任务耗时差异较大。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestNewWorkStealingPool {

    public static void main(String[] args) {
        //创建 4个工作线程的 任务可窃取线程池,如果不设置并行数,默认取 CPU 总核数
        ExecutorService workStealingThreadPool = Executors.newWorkStealingPool(4);
        
        for (int i = 0; i <10; i++) {
            final int index = i;
            workStealingThreadPool.execute(() -> {
                try {
                    //模拟任务执行时间为 任务编号为0 1 2 的执行时间需要 3秒;其余任务200 毫秒,导致任务时间差异较大
                    if (index <= 2) {
                        Thread.sleep(3000);
                    } else {
                        Thread.sleep(200);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " index:" + index);
            });
        }
        
        try {
            Thread.sleep(10000);//休眠 10 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("10秒后...");
    }
    
}

结果:线程 ForkJoinPool-1-worker-0 把3-9的任务都执行完了。

ForkJoinPool-1-worker-0 index:3
ForkJoinPool-1-worker-0 index:4
ForkJoinPool-1-worker-0 index:5
ForkJoinPool-1-worker-0 index:6
ForkJoinPool-1-worker-0 index:7
ForkJoinPool-1-worker-0 index:8
ForkJoinPool-1-worker-0 index:9
ForkJoinPool-1-worker-1 index:0
ForkJoinPool-1-worker-3 index:2
ForkJoinPool-1-worker-2 index:1
10秒后...

以上,是Java面试题【Executors创建线程池有哪几种方式】的参考答案。

 

输出,是最好的学习方法

欢迎在评论区留下你的问题、笔记或知识点补充~

—end—

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧