首页 > 其他分享 >小米面试:如何实现优先级线程池?

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

时间:2024-05-20 16:18:17浏览次数:12  
标签:Task 优先级 priority 线程 new 小米 public

我们知道,线程池中的所有线程都是由统一的线程工厂来创建的,当我们指定线程工厂时,线程池中的所有线程会使用我们指定的线程工厂来创建线程;但如果没有指定线程工厂,则会使用默认的线程工厂 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);
        }
    }
}

以上程序的执行结果如下:
image.png
从上述结果和代码可以看出,我们添加任务的顺序是: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);
        }
    }
}

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

课后思考

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

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

标签:Task,优先级,priority,线程,new,小米,public
From: https://www.cnblogs.com/vipstone/p/18202253

相关文章

  • C++ 多线程编程要点总结
    C++多线程编程要点总结:选择合适的线程库:C++11引入了 <thread> 头文件,提供了对线程的原生支持。也可以使用第三方库,如Boost.Thread,它提供了更多高级功能和更好的跨平台兼容性。线程创建与管理:使用 std::thread 类创建新线程,并传入函数或可调用对象作为线程的入口......
  • 多个CPU--多核--核心线程数​理解
    今天在创建简单线程池时,使用Runtime.getRuntime().availableProcessors()有些不懂网上这么配置的理由,百度说是计算资源(逻辑核心数)与CPU有关,但是和CPU具体啥关系还是一知半解,今天通过看资料,整理一下我的理解。importcom.google.common.util.concurrent.ThreadFactoryBuild......
  • 多线程和多进程 - 初窥
    一、说明在平常工作中,我们使用top命令查看一台linux服务器的cpu使用情况时,会发现某个进程的cpu使用率会超过100%,这是为什么?二、举例实验环境为CentOS7.6+Python2.71.多线程、多进程在操作系统中的表现形式我们首先看两个例子,test1.py和test2.py,都是执行死循环,test1.py两......
  • 线程安全使用 HashMap 的四种技巧
    这篇文章,我们聊聊线程安全使用HashMap的四种技巧。1方法内部:每个线程使用单独的HashMap如下图,tomcat接收到到请求后,依次调用控制器Controller、服务层Service、数据库访问层的相关方法。每次访问服务层方法serviceMethod时,都会在方法体内部创建一个单独的HashMap,......
  • C++中 符号的优先级
    符号运算顺序::从左至右a++a--type()type{}a()a[].->从左至右!~++a--a+a-a(type)sizeof&a*anewnew[]deletedelete[]从右至左.*->*从左至右a*ba/ba%b从左至右a+ba-b从左至右<<>>从左至右<<=>>=从左至右==!......
  • PHP的多样化执行方式(parallel PHP多线程实现,原生协程实现,多进程实现,ZTS、NTS、TS又是
    进程、线程、协程进程:应用程序的启动实例,运行起的代码叫进程,有独立的内存空间,类比工厂的P个(P=1单进程,P>1多进程)车间。线程:线程是CPU调度的最小单位,是进程内的执行单元,多个线程共享所属进程的资源。类比车间内的T个员工(T=1单线程,T>1多线程)车间。协程:类似线程,协程是用户态(CPU受......
  • 一次惨痛的面试:“网易提前批,我被虚拟线程问倒了”
    一、写在开头昨晚收到一个粉丝在私信的留言如下:build哥,今天参加了网易的提前批,可以说是一次惨痛的面试体验......
  • 线程
    3.线程线程介绍线程是需要上下文环境的线程一定绑定在某个进程上的内核线程只有一个堆栈(在内核中创建线程如果不指定进程的话,默认是绑定在system进程)R3中线程有2个堆栈,在R3进R0的时候会切换堆栈,这时候用的就不是R3的堆栈而是R0的(R0和R3的上下文环境)线程没有cr3的概念,只有......
  • 网页布局------小米商城官网
    小米商城官网地址小米商城-Xiaomi14Ultra、RedmiK70、MIXFOLD3,小米电视官方网站一、头部1、效果图2、网页结构<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=devi......
  • pyqt5 子线程如何操作主线程GUI
    一.简介在使用pyqt5编写gui时遇到两个问题,会导致界面崩溃,今天就围绕这两个问题来简单说明和改进。1.在主线程中使用while无限循环会导致界面崩溃2.在子线程中操作主线程gui会导致界面崩溃二.步骤说明1.在主线程中使用while无限循环会导致界面崩溃1)错误代码importsysfr......