首页 > 其他分享 >多线程面试题

多线程面试题

时间:2023-06-19 10:11:58浏览次数:53  
标签:面试题 队列 创建 阻塞 任务 线程 多线程 CPU

1.多线程的主要参数有哪些,有什么用?

1)corePoolSize(核心线程数)

指的是长期存活的线程数。比如地主家的长工,无论这一年活多还是活少,都不会被辞退。

2)maximumPoolSize(最大线程数)

指的是线程池允许创建的最大线程数,其中包含核心线程数(最大线程数 >= 核心线程数)。比如地主家临时活太多,长工干不完,就找临时工来帮忙,那么最大线程数=长工数量+临时工数量。

3)keepAliveTime(空闲线程存活时间)

指的是空闲线程的存活时间。主要是当线程池中没有任务时,会销毁一批线程,减少资源的浪费。销毁的线程数=最大线程数=核心线程数。

4)unit(时间单位)

指的是空闲线程的存活时间的单位,包括天、小时、分钟、秒...

5)workQueue(线程任务池队列)

也叫阻塞队列,指的是线程池存放任务的队列,用来存放线程池中所有待执行的任务。常用的是由链表组成的有界阻塞队列(LinkedBlockingQueue)。

6)threadFactory(线程工厂)

指的是线程池创建线程时调用的工厂方法,通过此方法可以设置线程命名规则及优先级等信息。

public static void main(String[] args) {
    // 创建线程工厂
    ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            // 创建线程池中的线程
            Thread thread = new Thread(r);
            // 设置线程名称
            thread.setName("线程-" + r.hashCode());
            // 设置线程优先级
            thread.setPriority(Thread.MAX_PRIORITY);
            // 设置线程类型(守护线程、用户线程), false-用户线程
            thread.setDaemon(false);
            return thread;
        }
    };
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2,
            0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), threadFactory);
    threadPoolExecutor.submit(new Runnable() {
        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            System.out.println(String.format("线程: %s, 线程优先级: %d",
                    thread.getName(), thread.getPriority()));
        }
    });
}

7)handler(拒绝策略)

当线程池中的任务超过阻塞队列可存储的最大值时,采用的拒绝策略。

其默认的拒绝策略是AbortPolicy–拒绝并抛出异常。

2.线程池提交一个任务的流程是什么样的? 

其流程图如下

当提交一个新任务(Runnable)时,会进行以下的步骤(也是原理):

1)如果线程数小于核心线程数,则直接创建新的线程,并将当前任务作为要执行的第一个任务;

2)如果线程数大于等于核心线程数,则会尝试将当前任务加入到阻塞队列,在加入时也需要判断阻塞队列是否已满;

3)如果阻塞队列未满,则直接将任务入队,其即可等待被执行;

4)如果阻塞队列已满,则判断线程数是否大于最大线程数;

5)如果线程数大于等于最大线程数则会通过 handler所指定的策略来处理此任务;

6)如果线程数大于最大线程数则,则会创建新的线程,并将当前任务作为要执行的第一个任务。

如果线程数大于核心线程数,并且有线程的空闲时间超过了设置的存活时间,那么这些线程会被销毁。

3.阻塞队列的作用是什么?为什么是先添加队列而不是先创建最大线程?

作用:

可以保证阻塞队列中没有任务时阻塞 获取任务的线程,使线程进入wait状态,释放CPU资源;

另外,阻塞队列自带阻塞和唤醒功能,不需要额外处理,当无任务时,线程池利用阻塞队列的take()挂起,维持核心线程的存活,不至于一直占用CPU资源。

先添加队列再创建最大线程:

因为在创建线程时,会获取全局锁,这时其他的线程就被阻塞了,影响了整体的效率。因此,把创建最大线程放在了后面,先让其入队,如果队列满了才会创建线程。阻塞队列分担了一些任务,那么就减少了线程的创建所造成的资源消耗,提供了效率。

比如⼀个企业⾥⾯有10个(core)正式⼯的名额,最多招10个正式⼯,要是任务超过正式⼯⼈数(task > core)的情况下,⼯⼚领导(线程池)不是⾸先扩招⼯⼈,还是这10⼈,但是任务可以稍微积压⼀下,即先放到队列去(代价低)。10个正式⼯慢慢⼲,迟早会⼲完的,要是任务还在继续增加,超过正式⼯的加班忍耐极限了(队列满了),就的招外包帮忙了(注意是临时⼯)要是正式⼯加上外包还是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。

4.线程池中线程复⽤原理

同一个线程可以从阻塞队列中不断获取任务来执行,其核心在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()方法,而是让每一个线程去执行一个“循环任务”,这个循环任务中不停的坚持是否有任务需要执行,如果有则直接执行,调用run()方法。通过这种方式就减少了频繁的创建线程造成的资源浪费,提高了效率。

5.核心线程数,最大线程数怎么设置?

线程数设置和CPU的核数是相关的,不同类型的任务,设置的方式不同。线程池执行的任务分为三种情况:

1)CPU密集型任务:存在大量的计算。核心线程数=CPU核数+1,最大线程数=CPU数量+1。(CPU核数和CPU数量是不同的)

正常情况下核心线程数等于CPU核数是最佳的,但会存在某个线程因意外情况发生中断,那么此时多出的一个线程就可以继续利用此CPU,这样效率是最高的,故核心线程数比CPU核数多1。

2)IO密集型任务:主要是IO操作,如文件IO和网络IO。核心线程数=CPU核数*2,最大线程数=CPU数量*2+1。

执行IO操作是比较耗时的,当某些线程在阻塞时,其对应的CPU就可以去执行其他的线程,通常核心数是CPU核数的2倍。但也不是绝对的,因为有可能这2倍的线程全被阻塞。当然下面的计算公式更为合适:核心线程数=CPU核数*(1+线程等待时间(也叫阻塞时间) / 线程运行总时间)。

3)复合型任务

6.如何优雅的停止一个线程?

一般不会使用stop()来停止线程,这种方式简单粗暴,不知道线程执行到哪一步了,该释放的锁都释放了吗?这都是未知的,所以通常使用interrupted()来中断线程,然后在创建线程的内部根据线程中断的标志来判断业务逻辑是否结束。

标签:面试题,队列,创建,阻塞,任务,线程,多线程,CPU
From: https://www.cnblogs.com/zys2019/p/17411171.html

相关文章

  • 用户态多线程实现的基本原理
    本文参考了用户态非抢占式线程库实现一文以及GNUPth。前者是一种用户态线程库的简单实现,属于一个很好的demo,后者就是大家熟知的Pthread的用户态实现,比较完善。 Keywords:User-SpaceMultiThreading,Pth 所谓多线程,简单讲就是能够让几个不同的代码片段轮流执行。内核实现多线......
  • std::thread 六:多线程&单例类
     为了避免单例类在多线程中重复的创建,下面提供了两种解决方法:1.互斥锁+双重检查2.std::call_once() 方法一:互斥锁+双重检查#include<iostream>#include<thread>#include<mutex>#include<list>usingnamespacestd;std::mutexmy_mutex;//创建一个单......
  • Java多线程-Lesson01-线程的创建
    线程创建的三种方式继承Thread类步骤:继承Thread类重写run()方法调用start()开启线程重写run()方法:@Overridepublicvoidrun(){for(inti=0;i<200;i++){System.out.println("run():"+i);}} run()方法里面就是我们多......
  • Java集合框架常见面试题
    剖析⾯试最常⻅问题之Java集合框架集合概述Java集合概览从下图可以看出,在Java中除了以Map结尾的类之外,其他类都实现了Collection接⼝。并且,以Map结尾的类都实现了Map接⼝。说说List,Set,Map三者的区别?List(对付顺序的好帮⼿):存储的元素是有序的、可重复的......
  • 多线程开启gprof性能测试的简易方法
    用到gprof时才知道,原来gprof只能对主线程统计耗时。manual上也没写线程相关的问题啊?不过有现成的解决方案:http://sam.zoy.org/writings/programming/gprof.html该方案封装了pthread_create(),让线程初始化执行一个setitimer(ITIMER_PROF,...)。简易的方法是直接在代码中写个setit......
  • react经典面试题解析--持续更新--day01
    一、类组件和函数组件的区别(面试常考)简单理解(所有同学都要掌握)1、类组件有生命周期,函数组件没有2、类组件需要继承Class,函数组件不需要3、类组件可以获取实例化的this,并且基于this做各种操作,函数组件不行4、类组件内部可以定义并维护state,函数组件都称为无状态了,那肯定......
  • 多线程
    多线程线程介绍每个进程都会有一个主线程,在创建进程时创建,往后创建的线程都属于子线程;线程在进程里不断抢占运行时间片;当进程遇到return结束,所有的线程全部结束。线程分类线程主要分为用户级线程和内核级线程用户级线程主要解决上下文切换问题,其调度由用户控制内核级线程......
  • 多线程
    1.进程和线程的定义进程:引用程序的执行实例(一个应用对应一个进程)线程:CPU调用和分派的基本单元,进程中执行运算的最小单位2.创建线程的种类继承java.lang.Thread类实现java.lang.Runnable接口3.继承java.lang.Thread类(1)定义MyThread类继承Thread类(2)重写run()方法,编写线程......
  • web worker进程和线程的区别,Chrome 中有哪些常⻅进程,如果我有⼀个耗时很⻓的同步计算
    进程(Process)和线程(Thread)都是操作系统中用于多任务处理的概念。简单地说,一个进程就是一个程序的执行空间,而一个线程则是在执行空间内独立运行的执行路径。区别:进程是系统分配资源的最小单位,线程是操作系统调度的最小单位。各个进程之间是独立的,各个线程之间共享一些资源。创......
  • c++多线程 std::async std::future
    c++标准库中对线程操作有完善的封装,其中最常用到的如std::thread,std::async。EffectiveModernCpp中指出,应尽量使用std::async即基于任务的编程而非基于线程的编程。std::thread在前面的文章有提到过,此处仅对std::async作以记录。正如前面所说,std::async是基于任务的策略,本人理......