写点什么

小米面试:如何实现优先级线程池?

作者:王磊
  • 2024-05-20
    陕西
  • 本文字数:3114 字

    阅读完需:约 10 分钟

小米面试:如何实现优先级线程池?

我们知道,线程池中的所有线程都是由统一的线程工厂来创建的,当我们指定线程工厂时,线程池中的所有线程会使用我们指定的线程工厂来创建线程;但如果没有指定线程工厂,则会使用默认的线程工厂 DefaultThreadFactory 来创建线程,核心源码如下:


DefaultThreadFactory() {    @SuppressWarnings("removal")    SecurityManager s = System.getSecurityManager();    group = (s != null) ? s.getThreadGroup() :                          Thread.currentThread().getThreadGroup();    namePrefix = "pool-" +                  poolNumber.getAndIncrement() +                 "-thread-";}
复制代码


那么问题来了,面试官问的是“如何实现优先级线程池?”,为什么我们一上来先讲了线程工厂呢?


这是因为,当我们讲到线程池优先级的时候,我们首先会想到线程的优先级,所以按照惯性思考,当面试官问到如何使用实现优先级线程池时,我们首先会考虑是不是在创建线程池的时候,可以通过某种方法来创建不同的线程优先级,从而实现优先级线程池?这就是开头我们一上来就讲线程工厂的原因。


那在线程工厂中如何设置线程的优先级呢?


它的设置也比较简单,如下代码所示:


import java.util.concurrent.ThreadFactory;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;
public class CustomThreadPoolExecutorDemo { public static void main(String[] args) { // 自定义线程工厂 ThreadFactory threadFactory = new CustomThreadFactory(); // 创建线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory); // 提交任务 executor.execute(() -> System.out.println("Task 1")); executor.execute(() -> System.out.println("Task 2")); // 关闭线程池 executor.shutdown(); }
static class CustomThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); // 设置线程优先级为最低优先级 thread.setPriority(Thread.MIN_PRIORITY); return thread; } }}
复制代码


但是这种方式也有问题,那就是线程工厂是统一的,所以即使能在线程工厂中设置线程的优先级,那么也是将整个线程池中的所有线程都设置成统一的优先级了,而不能解决咱们本文提出的问题的,那如何才能实现优先级线程池呢?

1.优先级线程池实现思路

转念一想,既然不能在线程优先级上下功夫,但我们是否可以在线程池的任务队列上动点心思呢?


此时我们想到,可以使用 PriorityBlockingQueue 优先级队列来对任务进行排序啊(PriorityBlockingQueue 天生支持按照优先级自动排序任务的),这样不就能保证优先级高的任务会被线程池优先获取并执行了嘛


所以,有时候一条路走不通的时候,我们可以尝试换一个思路再试试。

2.优先级队列使用

我们先来测试一下 PriorityBlockingQueue 的使用,以尝试其可行性,示例代码如下:


import java.util.concurrent.PriorityBlockingQueue;
public class PriorityBlockingQueueExample { public static void main(String[] args) { PriorityBlockingQueue<Task> priorityQueue = new PriorityBlockingQueue<>();
// 添加任务到优先级队列 priorityQueue.add(new Task("Task 1", 1)); priorityQueue.add(new Task("Task 4", 4)); priorityQueue.add(new Task("Task 3", 3)); priorityQueue.add(new Task("Task 2", 2));
// 从优先级队列中取出任务并执行 while (!priorityQueue.isEmpty()) { Task task = priorityQueue.poll(); if (task != null) { task.execute(); } } }
static class Task implements Comparable<Task> { private String name; private int priority;
public Task(String name, int priority) { this.name = name; this.priority = priority; }
public void execute() { System.out.println("Executing task: " + name); }
@Override public int compareTo(Task o) { return Integer.compare(this.priority, o.priority); } }}
复制代码


以上程序的执行结果如下:



从上述结果和代码可以看出,我们添加任务的顺序是:1、4、3、2,但最终会按照优先级排队执行的顺序是:1、2、3、4,执行结果符合我们的预期,优先级高的任务先被执行了(数字越小,优先级越高)。

3.优先级线程池

因此,我们实现的优先级线程池的最终代码如下:


import java.util.concurrent.BlockingQueue;import java.util.concurrent.PriorityBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;
public class PriorityThreadPool { public static void main(String[] args) { BlockingQueue<Runnable> queue = new PriorityBlockingQueue<>(1000);
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, queue );
for (int i = 0; i < 100; i++) { int finalI = i; executor.execute(new PriorityTask(i, () -> { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("优先级:" + finalI); })); } }
static class PriorityTask implements Runnable, Comparable<PriorityTask> { private final int priority; private final Runnable task;
public PriorityTask(int priority, Runnable task) { this.priority = priority; this.task = task; }
@Override public void run() { task.run(); }
@Override public int compareTo(PriorityTask other) { // 优先级高的任务应该排在前面(数字越小优先级越大) return Integer.compare(this.priority, other.priority); } }}
复制代码


以上程序执行结果如下:



从上述结果可以看出,线程池是完全按照优先级从高到低的顺序执行的(数字越小优先级越高),如果将 compareTo 中的排序方法倒置之后,那么线程池的执行顺序就完全相反了,可见使用 PriorityBlockingQueue 实现优先级线程池的效果非常显著。

课后思考

那么问题来了,PriorityBlockingQueue 在并发环境下会有线程安全问题吗?PriorityBlockingQueue 底层是如何保证线程安全的?


本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

用户头像

王磊

关注

公众号:Java中文社群 2018-08-25 加入

公众号:Java中文社群

评论

发布
暂无评论
小米面试:如何实现优先级线程池?_Java_王磊_InfoQ写作社区