当前位置: 首页 > news >正文

网站开发提问大连旅顺口旅游攻略

网站开发提问,大连旅顺口旅游攻略,郑州专业seo推荐,wordpress用户数据备份【更多面试资料请加微信号#xff1a;suns45】 https://flowus.cn/share/f6cd2cbe-627a-435f-a6e5-1395333f92e8 【FlowUs 息流】#x1f4e3;suns-Java资料 访问密码#xff1a;【请加微信号#xff1a;suns45】 ————线程相关的面试题———— 0#xff1a;创建线…【更多面试资料请加微信号suns45】 https://flowus.cn/share/f6cd2cbe-627a-435f-a6e5-1395333f92e8 【FlowUs 息流】suns-Java资料 访问密码【请加微信号suns45】 ————线程相关的面试题———— 0创建线程的四种方法 1、继承 Thread 类创建线程类 2、实现 Runnable 接口创建线程目标类 3、使用 Callable 和 FutureTask 4、通过线程池创建线程 1线程的状态有哪些 NEW新建 RUNABLE就绪运行 BLOCKED阻塞 WAITING等待 TIMED_WAITING计时等待 TEMINATED结束 2请说明线程状态之间是如何切换的 1NEW(新建) 1.1新建线程时状态是NEW。 2RUNABLE(就绪运行) 2.1调用Thread.start()就会由NEW-RUNABLE状态,这里抛出看(问题3) 提示这里在面试的时候可以直接和面试官聊这个问题三 3BLOCKED(阻塞) 3.1等待进入Synchronized方法/Synchronized块由RUNABLE就绪运行-BLOCKED阻塞 3.2当获取到monitor锁时由BLOCKED-RUNNABLE(就绪运行) 4WAITING(等待) 4.1RUNNABLE(就绪运行)-------------------WAITING(等待) Object.wait() Object.join() LockSupport.park() 4.2RUNNABLE(就绪运行)-------------------WAITING(等待) Object.notify() Object.notifyAll() LockSupport.unpack(Thread) 5TIMED_WAITING(计时等待) 5.1RUNNABLE(就绪运行)-------------------TIMED_WAITING(计时等待) Thread.sleep(long) Object.wait(long) Object.join(long) LockSupport.parkNacos(long) LockSupport.parkUntil(long) 5.2RUNNABLE(就绪运行)-------------------TIMED_WAITING(计时等待) Object.notify() Object.notifyAll() LockSupport.unpark(Thread) 6TERMINATED(终止) 6.1线程执行完成/异常终止 RUNNABLE(就绪运行) - TERMINATED终止 3请描述下Java线程状态和操作系统中的线程状态有所不同的地方 从运行过程来说 1:如果线程处于就绪状态其实就是在等待系统调度获取时间片进入运行状态的线程在CPU时间片用完之后又回到了就绪状态等待CPU下一次调度就这样操作系统线程在就绪状态和执行状态之间被系统反复的调度直到线程的代码逻辑执行完成/异常终止这时线程进入最后的状态TERMINATED(终止状态) 从状态划分来说 2: 就绪状态READY和运行状态RUNNING都是操作系统中的线程在java语言中没有区分这两种状态而是将这两种状态合并成了RUNNABLE(就绪运行) 补充 3: 在Thread.State枚举类中没有定义线程的READY(就绪状态)和RUNNING(运行状态),只有RUNNABLE可执行状态 总之NEW的Thread实例调用了start()之后进入线程RUNNABLE(就绪运行状态)但是run方法不一定会马上被并发执行需要获取CPU时间片之后才算真正启动执行。 4如何捕获线程异常 总结给线程设置异常处理器 public class ThreadCatchProcess4 implements Thread.UncaughtExceptionHandler {private String name;public ThreadCatchProcess3(String name) {this.name name;}Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(线程异常终止进程 t.getName());System.out.println(name 捕获了异常 t.getName() 异常); } }public class ThreadCatchProcess5 implements Runnable {public static void main(String[] args) throws InterruptedException {Thread.setDefaultUncaughtExceptionHandler(new ThreadCatchProcess3(获取异常));new Thread(new ThreadCatchProcess5(), MyThread-1).start();Thread.sleep(300);new Thread(new ThreadCatchProcess5(), MyThread-2).start();Thread.sleep(300);new Thread(new ThreadCatchProcess5(), MyThread-3).start();}Overridepublic void run() {throw new RuntimeException();} }5wait和sleep的异同点 相同点 wait和sleep方法都可以是线程阻塞对应线程状态是Waiting或Time_Waiting wait和sleep方法都可以响应中断Thread.interrupt() 区别点 wait方法的执行必须在同步方法(synchronized方法/代码块)中进行而sleep则不需要。 在同步方法里执行sleep方法时不会释放monitor锁但是wait会释放monitor锁 sleep方法短暂睡眠到指定时间后主动退出阻塞而没有指定时间的wait方法需要其他线程中断后才能退出阻塞 wait()和notify(),notifyAll()是object类的方法sleep()和yield()是Thread类的方法 6进程与线程的区别 1、一个进程由一个线程或多个线程组成一个进程至少有一个线程 2、线程是CPU调度的最小单位进程是操作系统分配资源的最小单位线程的划分尺度小于进程。 3、线程是出于高并发的调度诉求从进程内部演进而来线程的出现既充分发挥CPU的计算性能弥补进程调度的过于笨重。 4、进程之间是相互独立的但进程内部各个线程之间并不完全独立各个线程共享进程的方法区内存堆内存系统资源文件句柄系统信号等等 5、切换速度不同线程上下文切换比进程上下文切换要快很多。线程也称之为轻量级进程 7线程的优先级是否靠谱 PriorityDemo.class [PriorityDemo.main]thread-1-优先级为1机会值为721691578 [PriorityDemo.main]thread-2-优先级为2机会值为722963687 [PriorityDemo.main]thread-3-优先级为3机会值为723929277 [PriorityDemo.main]thread-4-优先级为4机会值为721130882 [PriorityDemo.main]thread-5-优先级为5机会值为732331398 [PriorityDemo.main]thread-6-优先级为6机会值为728891632 [PriorityDemo.main]thread-7-优先级为7机会值为734037128 [PriorityDemo.main]thread-8-优先级为8机会值为739307473 [PriorityDemo.main]thread-9-优先级为9机会值为733526049 [PriorityDemo.main]thread-10-优先级为10机会值为735961151总结 1、 整体而言高优先级的线程获得的执行机会更多。 在实例中可以看到优先级在 6 级以上的线程和 4 级以下的线程执行机会明细偏多整体对比非常明显。 2、 执行机会的获取具有随机性优先级高的不一定获得机会多。 比如例子中的 thread-9 比 thread-8 优先级高但是 thread-9 所获得的机会反而偏少。 8讲一下你对thread.interrupt()的理解 1、interrupt()其本身并不是一个强制打断线程的方法,其仅仅会修改线程的interrupt标志位需要线程自行去读标志位自行判断是否需要中断 2、Object.wait()和thread.sleep()都可以响应中断thread.interrupt() 9讲一下你对thread.join()的理解 有a和b两个线程当执行a线程时调用b.join()之后需要等待b线程执行完毕才能继续执行a线程。 优缺点 优点使用比较简单 缺点没有办法直接取得乙方线程的执行结果 ————线程池相关的面试题———— 10使用线程池的好处 当面试官问到线程池的好处时你可以这样回答 使用线程池有以下几个好处 降低资源消耗线程的创建和销毁是比较消耗资源的操作使用线程池可以重复利用已经创建的线程减少了频繁创建和销毁线程的开销从而降低了系统的资源消耗。 提高响应速度线程池中的线程可以被立即分配从而减少了线程创建的延迟时间使得系统能够更快地响应用户请求提高了系统的响应速度。 控制并发数线程池可以对并发线程数量进行限制避免因为过多线程导致系统资源耗尽或者性能下降。通过合理配置线程池的线程数可以根据系统资源和负载情况有效地控制并发数保证系统的稳定性和可靠性。 提供任务队列线程池通常还提供了任务队列可以将任务按照一定的策略进行排队实现对任务的异步执行。任务队列可以有效地缓冲任务平衡系统的资源占用情况保证系统的高效运行。 统一管理和监控线程池提供了对线程的统一管理和监控机制可以方便地监控线程的创建和销毁情况线程的执行状态和性能指标等为系统的优化和故障排查提供了便利。 综上所述使用线程池可以降低资源消耗、提高系统的响应速度、控制并发数、提供任务队列以及统一管理和监控线程从而在多线程环境下提升系统的性能和稳定性。 11请说下你对系统自带线程池类的看法 当面试官问到你对系统自带线程池类的看法时你可以这样回答 针对不同的系统自带线程池类我可以提供如下观点 newSingleThreadExecutor(): 作用创建一个只有一个线程的线程池。 特点线程池中只有一个线程可以按顺序执行任务。 缺点如果任务提交速度持续大于处理速度可能导致队列中大量的任务等待可能导致内存资源耗尽。 newFixedThreadPool(int Threads): 作用创建固定大小的线程池。 特点线程池大小固定每次提交新任务都会创建线程直到达到最大线程数。 缺点如果任务提交速度持续大于处理速度可能导致队列中大量的任务等待可能导致内存资源耗尽。 newCachedThreadPool(): 作用创建一个不限制线程数量的线程池。 特点线程池根据任务数量动态创建线程适用于任务数量比较大且变化较大的情况。 缺点最大线程数量无上限在任务提交较多的情况下可能导致CPU资源耗尽。 newScheduledThreadPool(): 作用创建可定期或延时执行任务的线程池。 特点可以按照预定时间或延时来执行任务非常适用于需要定时执行任务的场景。 缺点最大线程数量无上限线程数量不受限制当到期任务过多时可能导致CPU资源耗尽。 需要注意的是无论使用哪种线程池类都需要根据具体业务需求和系统性能来灵活选择和配置。合适的线程池配置可以提高系统的并发性能和资源利用率。 12请你说下构建线程池的几个参数的含义 当面试官问到构建线程池的几个参数的含义时你可以这样回答 在构建线程池时通常需要设置以下几个参数来控制线程池的行为和性能 核心线程数corePoolSize核心线程数是线程池中始终保持活动状态的线程数量。即使这些线程当前没有任务执行它们也不会被回收。核心线程数用于处理提交的任务当任务数超过核心线程数时线程池会创建新的线程来处理任务。 最大线程数maximumPoolSize最大线程数是线程池中允许存在的最大线程数量。当任务数量超过核心线程数时并且任务队列已满时线程池会创建新的线程直到线程数量达到最大线程数。超过最大线程数的任务将会被拒绝执行或采取其他策略处理。 非核心线程闲置超时时间keepAliveTime当线程池中的线程数量超过核心线程数时闲置的非核心线程在经过一段时间keepAliveTime后会被回收。这样可以避免线程空闲时占用资源。 阻塞队列workQueue阻塞队列用于存储还未被线程执行的任务。当线程池中的线程数量达到核心线程数时新的任务会被放入阻塞队列中等待执行。线程池提供了多种种类的阻塞队列如有界队列如ArrayBlockingQueue和无界队列如LinkedBlockingQueue根据业务需求和性能要求选择适当的队列类型。 线程工厂threadFactory线程工厂用于创建新的线程。通过实现ThreadFactory接口可以自定义线程的创建过程如设定线程名称、优先级等。 拒绝策略handler当任务提交速度超过线程池的处理能力时可以采取拒绝策略来处理无法被线程池接受和处理的任务。系统提供了几种拒绝策略如拒绝并抛出异常、直接在调用者线程中执行等也可以自定义拒绝策略。 这些参数的合理配置对于线程池的性能和效率非常重要需要根据具体的业务需求和系统资源来进行调整。对于不同的应用场景可能需要针对性地调整这些参数以获得最佳的线程池性能。 public ThreadPoolExecutor( int corePoolSize //核心线程数即使线程空闲Idle也不会回收 (前提是不设置核心线程回收) , int maximumPoolSize// 线程数的上限 , long keepAliveTime // 线程最大空闲Idle时长 , TimeUnit unit // 时间单位 , BlockingQueueRunnable workQueue, // 任务的排队队列 , ThreadFactory threadFactory // 新线程的产生方式 , RejectedExecutionHandler handler //拒绝策略 ) 13请你说下系统自带的拒绝策略有哪些呢 AbortPolicy拒绝策略 使用该策略时如果线程池队列满了则新任务被拒绝并且会抛出 RejectedExecutionException异常。该策略是线程池的默认的拒绝策略。 DiscardPolicy抛弃策略 该策略是 AbortPolicy 的 Silent安静版本如果线程池队列满了新任务会直接被丢掉并且不会有任何异常抛出 DiscardOldestPolicy抛弃最老任务策略 也就是说如果队列满了会将最早进入队列的任务抛弃从队列中腾出空间再尝试加入队列。因为队列是队尾进队头出队头元素是最老的所以每次都是移除对头元素后再尝试入队。 CallerRunsPolicy调用者执行策略 在新任务被添加到线程池时如果添加失败那么提交任务线程会自己去执行该任务不会使用线程池中的线程去执行新任务。 自定义策略【实现RejectedExecutionHandler接口】 14请叙述线程池的执行过程 1当有任务加入的时候首先会判定核心线程数是否满了 2如果未满则创建线程满了的话就检查队列是否满了未满加入队列等待 3如果队列也满了则检查最大线程数如果当前未到达最大线程数则创建线程 4如果已经到达最大线程数则会根据拒绝策略去执行下面的逻辑 15请说下线程池调度器的钩子方法 1任务执行之前的钩子方法前钩子 protected void beforeExecute(Thread t, Runnable r) { } 2任务执行之后的钩子方法后钩子 protected void afterExecute(Runnable r, Throwable t) { } 3线程池终止时的钩子方法停止钩子 protected void terminated() { } 16请说下线程池的状态都有哪些 1RUNNINGrunning: 线程池创建之后的初始状态可以执行任务 2SHUTDOWNshutdown: 线程池不再接受新任务但是会将工作队列中的任务执行完毕。 3STOPstop: 线程池不再接受新任务也不会处理工作队列中的剩余任务并且将会中断所有工作线程。 4TIDYINGtidying: 该状态下所有任务都已经终止或者处理完成将会执行terminated()钩子方法 5TERMINATEDterminated: 执行完terminated()钩子方法之后的状态 17请叙述线程池状态变换的规则 1线程池创建之后状态为 RUNNING 2执行线程池的 shutdown 实例方法 running-shutdown 3执行线程池的 shutdownNow 实例方法 running-stop 4当线程池处于 shutdown 状态执行shutdownNow 方法shutdown- stop。 5等待线程池的所有工作线程停止工作队列清空之后stop-tidying。 6执行完terminated()钩子方法之后,tidying-terminated 18如何优雅的关闭线程池 大家可以结合 shutdown、shutdownNow、awaitTermination 三个方法去优雅关闭一个线程池大致分为以下几步 1:执行 shutdown 方法拒绝新任务的提交并等待所有任务有序执行完毕。 2:执行 awaitTerminationlong timeout,TimeUnit unit方法指定超时时间判断是否已经关闭所有任务线程池关闭完成。 3:如果 awaitTermination 方法返回 false或者被中断。调用 shutDownNow 方法立即关闭线程池所有任务。 4:补充执行 awaitTerminationlong timeout,TimeUnit unit方法判断线程池是否关闭完成。如果超时则可以进入循环关闭循环一定的次数如 1000 次不断关闭线程池直到其关闭或者循环结束。 19线程池如何调优 1、线程数 IO密集型线程池 原因 主要是执行IO操作执行IO操作时间较长导致CPU利用率下降这种任务CPU常处于空闲状态。 特点 1设置allowCoreThreadTimeOut…方法并且传入了参数true则KeepAliveTime参数设置的空闲超时策略也将被用于核心线程当池中的线程长时间空闲时可以自行销毁。 2使用有界队列防止内存溢出 3corePoolSize、maximumPoolSize保持一致使得在接收到新任务时如果核心线程满了不是优先加入队列而是优先创建新的线程去执行新的任务。 4使用懒汉式创建线程池如果代码没有用到就不创建用到了再创建。 5使用JVM关闭时的钩子函数优雅的自动关闭线程池。 例子 Netty读写操作为此类任务的典型例子。 CPU密集型线程池 特点 CPU 密集型任务的并行执行的数量应当 CPU 的核心数1 原因 执行计算任务由于响应时间很快CPU一直在运行这种任务CPU的利用率高。 为什么线程数等于CPU核心数1呢? 为什么要把它设置为CPU的核心数加一呢理论上把它设置为CPU核心数。性能是最优的因为没有任何线程切换的开销同时呢又可以让每一个CPU的核心都忙起来。没有任何的资源浪费这样想当然没什么问题但问题是啊如果某一个县城突然出现暂停或者中断的话。那么CPU就会有一个核心处于空闲状态了所以呢我们一般会设置为n1这样多出来的一个线程。就可以用来充分利用CPU的空闲时间这就像是踢足球弄了一个候补队员对吧 混合型任务 例子 Web服务器的HTTP请求处理操作为此类任务的典型例子。 特点 在涉及混合型任务的情况下确定线程池数量的确相对复杂一些。上述提到的线程数量的计算公式为n×u×(1wt÷ST)其中 nCPU的核心数。 u期望的CPU利用率。根据实际需求和系统性能可以设置一个合理的值。 wt线程等待的时间。 ST线程运行的时间。 具体来说我们可以通过使用工具如jvisualvm进行性能分析来获取wt和ST的值。以下是一些步骤 运行你的项目并启动jvisualvm进行性能分析。 在jvisualvm中选择你的项目并点击Profile然后选择CPU。 等待一段时间进行性能分析以获取线程的运行时间ST。 通过分析得到的数据计算线程的自用时间ST - 线程运行时间。 使用得到的自用时间和其他参数将其带入线程数量计算公式得到线程池数量的估算值。 需要注意的是这个公式只是一个估算的参考值实际的线程池数量仍需要结合项目和实际需求进行调试和测试。在选择最终的线程池数量时还需要考虑系统性能、负载均衡、内存要求等因素。 2、队列大小调优 参考这个网站https://www.javacodegeeks.com/2012/03/threading-stories-about-robust-thread.html package com.threadTest; ​ import java.lang.management.ManagementFactory; import java.math.BigDecimal; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; ​ public class MyPool extends PoolSizeCalculator{public static void main(String[] args) throws InterruptedException,InstantiationException,IllegalAccessException,ClassNotFoundException {MyPool calculator new MyPool();calculator.calculateBoundaries(new BigDecimal(1.0),new BigDecimal(100000)); ​ThreadPoolExecutor pool new ThreadPoolExecutor(16, 16,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueueRunnable(2500));pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());}Overrideprotected long getCurrentThreadCPUTime() {return ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();}Overrideprotected Runnable creatTask() {return new AsynchronousTask();}Overrideprotected BlockingQueue createWorkQueue() {return new LinkedBlockingQueue();} }package com.threadTest; ​ public class AsynchronousTask implements Runnable {public AsynchronousTask() {} ​Overridepublic void run() {System.out.println(Thread.currentThread().getName());} }package com.threadTest; ​ import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.BlockingQueue; ​ /*** A class that calculates the optimal thread pool boundaries. It takes the desired target utilization and the desired* work queue memory consumption as input and retuns thread count and work queue capacity.** author Niklas Schlimm*/ public abstract class PoolSizeCalculator { ​/*** The sample queue size to calculate the size of a single {link Runnable} element.*/private final int SAMPLE_QUEUE_SIZE 1000; ​/*** Accuracy of test run. It must finish within 20ms of the testTime otherwise we retry the test. This could be* configurable.*/private final int EPSYLON 20; ​/*** Control variable for the CPU time investigation.*/private volatile boolean expired; ​/*** Time (millis) of the test run in the CPU time calculation.*/private final long testtime 3000; ​/*** Calculates the boundaries of a thread pool for a given {link Runnable}.** param targetUtilization the desired utilization of the CPUs (0 targetUtilization 1)* param targetQueueSizeBytes the desired maximum work queue size of the thread pool (bytes)*/protected void calculateBoundaries(BigDecimal targetUtilization, BigDecimal targetQueueSizeBytes) {calculateOptimalCapacity(targetQueueSizeBytes);Runnable task creatTask();start(task);start(task); // warm up phaselong cputime getCurrentThreadCPUTime();start(task); // test intervallcputime getCurrentThreadCPUTime() - cputime;long waittime (testtime * 1000000) - cputime;calculateOptimalThreadCount(cputime, waittime, targetUtilization);} ​private void calculateOptimalCapacity(BigDecimal targetQueueSizeBytes) {long mem calculateMemoryUsage();BigDecimal queueCapacity targetQueueSizeBytes.divide(new BigDecimal(mem), RoundingMode.HALF_UP);System.out.println(Target queue memory usage (bytes): targetQueueSizeBytes);System.out.println(createTask() produced creatTask().getClass().getName() which took mem bytes in a queue);System.out.println(Formula: targetQueueSizeBytes / mem);System.out.println(* Recommended queue capacity (bytes): queueCapacity);} ​/*** Brian Goetz optimal thread count formula, see Java Concurrency in Practice (chapter 8.2)** param cpu cpu time consumed by considered task* param wait wait time of considered task* param targetUtilization target utilization of the system*/private void calculateOptimalThreadCount(long cpu, long wait, BigDecimal targetUtilization) {BigDecimal waitTime new BigDecimal(wait);BigDecimal computeTime new BigDecimal(cpu);BigDecimal numberOfCPU new BigDecimal(Runtime.getRuntime().availableProcessors());BigDecimal optimalthreadcount numberOfCPU.multiply(targetUtilization).multiply(new BigDecimal(1).add(waitTime.divide(computeTime, RoundingMode.HALF_UP)));System.out.println(Number of CPU: numberOfCPU);System.out.println(Target utilization: targetUtilization);System.out.println(Elapsed time (nanos): (testtime * 1000000));System.out.println(Compute time (nanos): cpu);System.out.println(Wait time (nanos): wait);System.out.println(Formula: numberOfCPU * targetUtilization * (1 waitTime / computeTime ));System.out.println(* Optimal thread count: optimalthreadcount);} ​/*** Runs the {link Runnable} over a period defined in {link #testtime}. Based on Heinz Kabbutz ideas* (http://www.javaspecialists.eu/archive/Issue124.html).** param task the runnable under investigation*/public void start(Runnable task) {long start 0;int runs 0;do {if (runs 5) {throw new IllegalStateException(Test not accurate);}expired false;start System.currentTimeMillis();Timer timer new Timer();timer.schedule(new TimerTask() {public void run() {expired true;}}, testtime);while (!expired) {task.run();}start System.currentTimeMillis() - start;timer.cancel();} while (Math.abs(start - testtime) EPSYLON);collectGarbage(3);} ​private void collectGarbage(int times) {for (int i 0; i times; i) {System.gc();try {Thread.sleep(10);} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}} ​/*** Calculates the memory usage of a single element in a work queue. Based on Heinz Kabbutz ideas* (http://www.javaspecialists.eu/archive/Issue029.html).** return memory usage of a single {link Runnable} element in the thread pools work queue*/public long calculateMemoryUsage() {BlockingQueueRunnable queue createWorkQueue();for (int i 0; i SAMPLE_QUEUE_SIZE; i) {queue.add(creatTask());}long mem0 Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();long mem1 Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();queue null;collectGarbage(15);mem0 Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();queue createWorkQueue();for (int i 0; i SAMPLE_QUEUE_SIZE; i) {queue.add(creatTask());}collectGarbage(15);mem1 Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();return (mem1 - mem0) / SAMPLE_QUEUE_SIZE;} ​/*** Create your runnable task here.** return an instance of your runnable task under investigation*/protected abstract Runnable creatTask(); ​/*** Return an instance of the queue used in the thread pool.** return queue instance*/protected abstract BlockingQueueRunnable createWorkQueue(); ​/*** Calculate current cpu time. Various frameworks may be used here, depending on the operating system in use. (e.g.* http://www.hyperic.com/products/sigar). The more accurate the CPU time measurement, the more accurate the results* for thread count boundaries.** return current cpu time of current thread*/protected abstract long getCurrentThreadCPUTime(); ​ }20、说说你对线程池的理解 当别人问你对线程池的理解时给出如下回答 线程池是一种并发编程的技术它可以管理和复用多个线程用于执行异步任务。我对线程池的理解如下 首先线程池中最小的执行单位是WorkerWorker充当了任务的代理实现了Runnable接口和run方法。在Worker初始化时关键代码将当前Worker作为线程的构造器入参然后通过线程的start方法来执行Worker的run方法。Worker还实现了AQS所以它本身也是一个锁在执行任务时会锁住自己任务执行完成后会释放自己。 其次任务的提交过程涉及到execute方法和addWorker方法的执行。在执行execute方法时经过三个判断条件后会执行addWorker方法。在addWorker方法中通过线程的start方法来执行Worker线程的runWorker方法。runWorker方法中的getTask操作是一个阻塞操作它保证了核心线程和未超时的线程不会被销毁。在完成任务的过程中还有beforeExecute、task.run和afterExecute等方法的处理用于执行前的准备和执行后的清理。 通过了解线程池的内部实现机制我们可以更好地管理并发任务的执行。线程池通过复用线程、任务调度和控制并发度等功能提高了系统的性能和资源利用率并能够灵活应对不同的业务场景和负载变化。 21、ThreadPoolExecutor、Executor、ExecutorService、Runnable、Callable、FutureTask 之间的关系 ThreadPoolExecutor、Executor、ExecutorService、Runnable、Callable、FutureTask之间的关系可以通过下面的回答来解释 ThreadPoolExecutor是线程池的具体实现类它实现了ExecutorService接口。ExecutorService接口是继承自Executor接口的子接口它定义了一些更丰富的方法来管理和控制线程池。 Executor接口是Java线程池框架的根接口它只有一个方法execute(Runnable command)用于提交一个Runnable任务给线程池执行。Executor接口的实现类可以是ThreadPoolExecutor。 Runnable接口和Callable接口是用于表示任务的接口它们都是可被线程执行的任务。Runnable接口定义了一个run()方法用于执行任务而Callable接口定义了一个call()方法可以返回执行结果。 FutureTask是一种特殊的任务它实现了RunnableFuture接口而RunnableFuture接口则是Future接口和Runnable接口的组合。FutureTask可以用来包装一个Callable或Runnable任务并提供了Future和Runnable的接口特性。它可以被提交给线程池执行并通过Future对象获取任务的执行结果。 总结起来 Executor接口定义了线程池的基本执行方式其中的execute方法用于提交任务。 ThreadPoolExecutor是ExecutorService接口的一个具体实现它是线程池的实际执行者。 ExecutorService接口继承自Executor接口并且提供了更多的管理和控制线程池的方法。 Runnable接口和Callable接口是表示任务的接口其中Runnable接口用于执行无返回结果的任务而Callable接口用于执行有返回结果的任务。 FutureTask是一个特殊的任务实现它可以用来包装Callable或Runnable任务并提供了Future和Runnable的接口特性可以获取任务的执行结果。 22说一说队列在线程池中起的作用 当别人问队列在线程池中起的作用你可以结合上述回答给出如下回答 缓冲作用队列可以缓存提交的任务使得请求数量大于实际线程数时任务可以在队列中排队等待执行从而平衡请求数量和线程处理能力之间的差异。 任务调度作用队列根据调度规则确定任务的执行顺序通过先进先出、优先级、延迟等策略有序地调度任务的执行。 并发度控制作用队列用于控制并发度当线程数达到最大限制时多余的任务会被放入队列中等待执行限制了同时执行的任务数量。 阻塞机制队列的阻塞功能使得线程在执行完所有任务后不会自动终止而是等待队列中有新任务产生后立即被消费。 23结合请求不断增加时说一说线程池构造器参数的含义和表现 答线程池构造器各个参数的含义如下 coreSize 核心线程数 maxSize 最大线程数 keepAliveTime 线程空闲的最大时间 queue 有多种队列可供选择比如 SynchronousQueue为了避免任务被拒绝要求线程池的 maxSize 无界缺点是当任务提交的速度超过消费的速度时可能出现无限制的线程增长 LinkedBlockingQueue无界队列未消费的任务可以在队列中等待 ArrayBlockingQueue,有界队列可以防止资源被耗尽 线程新建的 ThreadFactory 可以自定义也可以使用默认的 DefaultThreadFactoryDefaultThreadFactory 创建线程时优先级会被限制成 NORM_PRIORITY默认会被设置成非守护线程 在 Executor 已经关闭或对最大线程和最大队列都使用饱和时可以使用RejectedExecutionHandler 类进行异常捕捉有如下四种处理策略 ThreadPoolExecutor.AbortPolicy 拒绝策略 ThreadPoolExecutor.DiscardPolicy 抛弃策略 ThreadPoolExecutor.CallerRunsPolicy 调用者执行策略 ThreadPoolExecutor.DiscardOldestPolicy 抛弃最老任务策略 自定义策略 当请求不断增加时各个参数起的作用如下 请求数 coreSize创建新的线程来处理任务 coreSize 请求数 能够成功入队列任务进入到队列中等待被消费 队列已满 请求数 maxSize创建新的线程来处理任务 队列已满 请求数 maxSize使用 RejectedExecutionHandler 类拒绝请求。 24coreSize 和 maxSize可以动态设置么有没有规则限制 coreSize和maxSize都是线程池的线程数量相关的参数它们可以进行动态设置但是需要遵循一些规则限制。以下是对这个问题的回答 coreSize的动态设置 在大多数线程池实现中coreSize是可以进行动态设置的即在运行过程中可以改变coreSize的值。但需要注意的是动态设置coreSize可能会影响线程池的整体性能和稳定性因此应该谨慎操作。特别是在已经提交了一些任务的情况下如果将coreSize减小可能导致已提交任务无法得到处理。 maxSize的动态设置 相比coreSizemaxSize的动态设置要更加复杂一些。在某些线程池实现中maxSize也可以进行动态设置但需要注意的是maxSize的值不能小于coreSize。另外动态增大maxSize可能会对系统资源产生压力应慎重考虑。 需要说明的是每个线程池实现可能会对coreSize和maxSize的可设置范围有不同的规则限制具体取决于线程池的实现和设计。 总结起来coreSize和maxSize可以进行动态设置但操作应慎重并且需要遵循一些规则限制比如不能将coreSize减小以及maxSize不能小于coreSize。 25说一说对于线程空闲回收的理解源码中如何体现的 线程空闲回收是指当线程在一段时间内没有任务可执行时线程池会将这些空闲的线程回收以节省资源和提高效率。在源码中线程空闲回收的实现可以体现在以下几个方面 空闲线程回收时机 当线程超过keepAliveTime时间后如果它在阻塞队列中找不到可执行的任务即线程处于空闲状态当前线程就会被回收。这是通过对线程池的定时检查和控制实现的。 core thread的回收条件 如果allowCoreThreadTimeOut设置为true即使是core线程也会被回收直到只剩下一个线程为止。如果allowCoreThreadTimeOut设置为false则只会回收非core线程。 阻塞中断机制 线程在任务执行完成后之所以没有立即终止是因为它阻塞在队列中等待任务。但是如果在keepAliveTime时间内仍然未能获取到任务线程会被中断阻塞并直接返回从而结束线程的生命周期。JVM会回收被中断的线程对象。 综上所述线程空闲回收的实现源码中体现在对线程的定时检查和控制并通过中断机制打破阻塞实现线程的回收。 26如果我想在线程池任务执行之前和之后做一些资源清理的工作可以么如何做 答可以的ThreadPoolExecutor 提供了一些钩子函数我们只需要继承 ThreadPoolExecutor 并实现这些钩子函数即可。在线程池任务执行之前实现 beforeExecute 方法执行之后实现 afterExecute方法。 27线程池中的线程创建拒绝请求可以自定义实现么如何自定义 答可以自定义的线程创建默认使用的是 DefaultThreadFactory自定义话的只需要实现ThreadFactory 接口即可拒绝请求也是可以自定义的实现 RejectedExecutionHandler 接口即可在 ThreadPoolExecutor 初始化时将两个自定义类作为构造器的入参传递给 ThreadPoolExecutor 即可 28说说你对线程池Worker 的理解 Worker是线程池中的工作线程它充当了任务的代理负责执行提交给线程池的任务。以下是对Worker的进一步说明 实现Runnable接口Worker实现了Runnable接口通过实现run方法来执行任务的具体逻辑。在初始化时通过this.thread getThreadFactory().newThread(this)将当前Worker作为线程的构造器入参创建与Worker关联的线程实例。 执行任务通过t.start()启动线程实际上是执行Worker的run方法。在run方法中Worker从任务队列中获取任务并调用任务的run方法执行任务逻辑。 锁的实现Worker本身也实现了AbstractQueuedSynchronizerAQS它可以拥有独立的锁。在执行任务期间Worker会锁住自身以避免其他线程同时执行任务。任务执行完毕后Worker会释放自身的锁。 综上所述Worker在线程池中充当着任务的代理角色它实现了Runnable接口并在初始化时与一个线程关联。通过调用线程的start方法来执行Worker的run方法从而执行任务的具体逻辑。Worker还具备锁的功能通过锁定自身来确保任务的独占执行。 29说一说 submit方法执行的过程 submit方法是用于向线程池提交任务的方法它将任务提交给线程池进行异步执行。 将任务封装为一个Future对象submit方法接收一个Callable或Runnable类型的参数它会将这个任务封装为一个Future对象。Future是用来表示异步计算结果的它可以用来获取任务的执行结果或取消任务的执行。 决定任务的执行策略线程池会根据预先设置的策略来决定任务的执行策略。具体的策略包括但不限于选择合适的线程来执行任务、将任务放入任务队列等待执行、拒绝执行任务等。 提交任务给线程池一旦任务被封装为Future对象并决定了执行策略后submit方法会将任务提交给线程池。线程池会根据具体的实现方式选择合适的线程或将任务放入任务队列中进行异步执行。 返回Future对象submit方法执行完毕后会立即返回一个Future对象它可以用于控制和获取任务的执行结果。通过Future对象可以判断任务是否已经完成、等待任务完成、获取任务的执行结果等。 需要注意的是submit方法是异步的它会立即返回不会等待任务的执行结果。如果需要等待任务完成并获取结果可以调用Future对象的相关方法如get方法进行阻塞等待。 综上所述submit方法将任务封装为Future对象并根据线程池的执行策略将任务提交给线程池进行异步执行。通过返回的Future对象可以控制和获取任务的执行结果。 30说一说线程执行任务之后都在干啥 在线程执行任务完成之后会进行下面两种操作中的一种 阻塞等待新任务线程会继续在任务队列中阻塞等待新的任务到来。如果任务队列中没有任务则线程会一直阻塞直到有新任务提交到队列中。这种方式可以保持线程的持续可用性以便随时处理新的任务。 线程终止和回收线程在执行完任务后如果任务队列中没有新的任务并且线程池的策略允许线程回收那么该线程可能会被终止并被JVM回收。这样可以避免空闲线程的资源浪费提高资源利用率。 需要注意的是具体采取哪种操作取决于线程池的实现和配置。一般来说线程池会根据预先设置的策略来决定如何处理空闲的线程以确保线程池的性能和资源利用的平衡。 31keepAliveTime 设置成负数或者是 0表示无限阻塞 当面试官问到如何表示线程池的空闲线程进行无限阻塞时可以回答如下要表示线程池的空闲线程无限阻塞而不被回收可以根据不同情况进行设置 若keepAliveTime参数表示线程空闲超时时间将其设置为负数或0意味着线程池中的空闲线程会保持存活状态不会被回收。负数表示所有线程都不会被回收即使处于空闲状态而0表示空闲线程会立即被回收。 若想要真正实现无限阻塞的效果可以将keepAliveTime参数设置为Long.MAX_VALUE即使用Long类型的最大值来表示。这样空闲线程将会无限期地保持存活状态直到有新的任务被提交到线程池中。 需要注意的是使用无限阻塞可能会占用较多的系统资源因此在实际应用中需要权衡资源的使用和性能的需求。通常情况下合理设置keepAliveTime参数使得线程池能够根据实际需求回收空闲线程能够更好地平衡资源利用和性能。 32说一说 Future.get 方法是如何拿到线程的执行结果的 当面试官问到如何使用 Future.get 方法来获取线程的执行结果时可以回答如下 Future.get 方法是用于获取线程的执行结果的。 具体来说当我们使用 submit 方法向线程池提交任务时得到的返回值是一个 Future 对象它是 FutureTask 类的一个实例。FutureTask 实现了 Future 接口并额外提供了一个 outcome 属性存储线程的执行结果。 当我们调用 Future.get 方法时它会阻塞当前线程直到任务执行完成并返回结果。实际上get 方法内部会检查任务是否已经完成如果任务已完成就直接从 FutureTask 的 outcome 属性中获取结果并返回如果任务未完成get 方法会等待任务完成。 一旦任务完成get 方法就会返回线程的执行结果。如果任务发生异常get 方法也会抛出相应的异常。 因此通过调用 Future.get 方法我们可以获取到线程的执行结果以便进一步处理或使用。 希望以上回答能够满足您的需求。如果还有其他问题请随时提问。 33线程池公用和独立 在实际工作中为了确保线程池的效率和业务场景的独立性一般会遵循以下优化原则 查询和写入分离: 查询和写入操作通常具有不同的特点和需求。由于查询量通常远大于写入量为了避免写入请求被查询请求拖延应该为查询和写入操作分别配置独立的线程池。这样可以确保查询请求能够及时处理而写入请求则可以在队列中排队等待。 每个业务场景独立使用线程池: 对于多个写入业务场景为了方便业务治理、限流和熔断等操作应该尽量避免共用线程池。每个业务场景单独使用自己的线程池能够保证业务场景之间的独立性避免相互影响。尽管现代的服务器具有较大的内存但为每个写入场景分配独立的线程池仍然是较为合理的做法。 相似的查询业务场景可以公用线程池: 相似的查询业务场景通常具有多个共同点例如查询的场景较多、处理时间较短、查询量较大等。对于这些相似的查询场景可以考虑将它们归为一类并共用一个线程池。这样做的好处是避免配置过多线程池带来的复杂性以及节约资源。 综上所述线程池的优化应考虑查询和写入的分离、每个业务场景独立使用线程池以及相似的查询场景共用线程池。这样可以更好地满足不同业务场景的需求提高系统的性能和可维护性。 总结 查询和写入不公用一个线程池因为查询需要及时处理而写入可以去队列中排队 多个写入场景一般不要公用一个线程池因为不同业务之间要做到互不影响 查询可以公用一个线程池1、因为查询的线程池中线程和队列大小毕竟复杂2、耗费资源 34线程大小和队列大小 在确定线程池的大小和队列大小时可以优化如下 根据业务流量进行考虑: 在初始化线程池时需要考虑当前所有业务的流量情况。如果所有业务都有大量的并发流量建议将线程池的大小和队列大小设置较小。这样可以避免因为线程过多导致资源耗尽或性能下降。相反如果业务的并发流量相对较少可以适当增加线程池的大小和队列大小。 根据业务的实时性要求进行设置: 根据业务对实时性的要求可以选择不同的线程池配置。如果业务对实时性要求较高可以设置线程池的核心线程数等于最大线程数并将最大线程数设置较大。这样可以确保任务能够立即得到处理而不需要排队等待。如果业务对实时性要求相对较低可以适当增加队列大小允许任务在队列中排队等待处理。 综上所述根据业务的流量和实时性要求优化线程池的大小和队列大小。如果业务并发流量大设置较小的线程池和队列大小如果业务对实时性要求高使用等于最大线程数的核心线程数和较大的最大线程数如果业务对实时性要求低可以适当增加队列大小。 35、线程池如何拥有回调功能 借助google的MoreExecutors 添加监听 Slf4jpublic class CallbackTaskScheduler {static ListeningExecutorService gPool null; ​static {ExecutorService jPool ThreadUtil.getMixedTargetThreadPool();gPool MoreExecutors.listeningDecorator(jPool);} ​private CallbackTaskScheduler() {} ​/*** 添加任务 * param executeTask*/public static R void add(CallbackTaskR executeTask) {ListenableFutureR future gPool.submit(new CallableR() {public R call() throws Exception {R r executeTask.execute();return r;}});Futures.addCallback(future, new FutureCallbackR() {public void onSuccess(R r) {executeTask.onBack(r);} ​public void onFailure(Throwable t) {executeTask.onException(t);}});}}ThreadLocal—————— 1、请你说下你对ThreadLocal的认知 1初始化 1:非空判断 //获取“线程本地变量”中当前线程所绑定的值 if (LOCAL_FOO.get() null) { //设置“线程本地变量”中当前线程所绑定的初始值 LOCAL_FOO.set(new Foo()); } - 2: ThreadLocal.withInitial(…)静态工厂方法### 2使用场景(优点)#### 从线程隔离的角度来考虑- 好处- 1线程安全在多线程环境下可以防止自己的变量被其他线程篡改- 2避免加锁提高执行效率 由于各个线程之间的数据相互隔离避免同步加锁带来的性能损失大大提升了并发性的性能。- 举例- 在“线程隔离”场景中使用 ThreadLocal 的典型案例为可以每个线程绑定一个数据库连接使得这个数据库连接为线程所独享从而避免数据库连接被混用而导致操作异常问题- 代码- 1、Hibernate 通 过 ThreadLocal 非常简单实现了数据库连接的安全使用。- 2、如果每个线程都需要打印时间会存在线程安全问题解决线程安全问题比喻一本老师笔记全班一起用。ThreadLocal复制30份解决全班一起用的问题#### 从跨函数传递数据来考虑- 好处- 1避免通过参数传递数据带来的高耦合- 举例- 可以每个线程绑定一个 Session用户会话信息这样一个线程的所有调用到的代码都可以非常方便地访问这个本地会话而不需要通过参数传递。- 代码- 1用来传递请求过程中的用户 ID。- 2用来传递请求过程中的用户会话Session。- 3用来传递 HTTP 的用户请求实例 HttpRequest。- 4其他需要在函数之间频繁传递的数据。### 3从jdk版本上来说- 1拥有者发生了变化- 新版本的ThreadLocalMap 拥有者 Thread代码层面上还是没变的早起版本的ThreadLocalMap 拥有者 为ThreadLocal- 2Key发生了变化- 新版本的Key为ThreadLocal实例,Value是ThreadLocal的值- 老版本的Key为Thread实例- 3ThreadLocalMap存储的Key-Value对数量变少了。- 新版本的ThreadLocalMap的Key为ThreadLocal实例多线程情况下ThreadLocal实例比线程数少。- 老版本的Key-Value对数量与线程个数强关联如果线程数量多则ThreadLocalMap存储 Key-Value对 数量也多。- 4threadLocalMap是否被销毁- 早期版本ThreadLocalMap的拥有者为ThreadLocal在Thread线程实例销毁后ThreadLocalMap还是存在的;- 新版本的ThreadLocalMap的拥有者为Thrad现在当Thread实例销毁后ThreadLocalMap也会随之销毁在一定程度上能减少内存的消耗。### 4使用 static final 修饰 ThreadLocal 对象的原因以及带来的坏处原因- 1:ThreadLocal实例作为ThreadLocalMap的Key针对一个线程内所有操作是共享的所以建议设置static修饰符以便被所有的对象共享。- 2:静态变量在类第一次被使用时装载只会分配一次存储空间此类所有的实例都会共享这个存储空间所以使用static修饰符ThreadLocal会节约内存空间- 3:为了确保ThreadLocal实例的唯一性除了使用static修饰外还会使用final加强修饰以防止其在使用过程中发生动态变更坏处- 使得Thread实例内部的ThreadLocalMap中Entry的Key在Thread实例的生命周期内将始终保持为非null,从而导致Key所在的Entry不会被自动清空,这就会导致Entry中的Value指向的对象一直存在强引用 Value指向的对象在线程生命期内不会被释放最终导致内存泄露所以使用static final修饰ThreadLocal实例使用完后必须使用remove()进行手动释放。
http://www.ho-use.cn/article/10815317.html

相关文章:

  • 整站seo排名公司wordpress两个侧边栏
  • 彩妆网站模板做视频找素材的网站
  • 网站里+动效是用什么做的网站app推广怎么做
  • 网站优化标题亚马逊网站开发
  • 模板网站区别企业建设网站需注意哪些内容
  • o2o网站开发框架中卫网站设计
  • 网站建立需要什么条件大连网站建设谁家好
  • 电影里的做视频在线观看网站软件开发工资一般多少
  • 进行seo网站建设网站编程入门
  • 达州+网站建设宁波seo快速优化
  • 甘肃省城乡住房建设厅网站首页盘州住房和城乡建设局网站
  • 营销型网站怎么收费标准网页设计基础的期末试卷和答案
  • 网站管理助手ftp连接不上外国人做网站
  • 夏邑做网站网站建设冫首先金手指十五
  • 医疗网站 seo怎么做wordpress升级php异常
  • 运河建设集团网站wordpress前端页面存放
  • 做网站用哪个服务器不用备案网络技术学什么
  • 湘西网站建设吧网站建设步骤大全
  • 茂名住房和城乡建设部网站网页设计与制作感受
  • 生活服务手机网站开发做网站济宁
  • 免费的行情软件app网站怎么用网网站模板做网站
  • 做网站运营如何提升用户粘度哈尔滨建设工程交易中心
  • 胶州建设信息网站网络运营是什么专业
  • 九五至尊娱乐场网站网站二次备案
  • 郑州专业的建网站响应式网站设计教程
  • 遵义网信办广告优化师前景怎样
  • 洛阳网站建设招聘信息做设计找图有哪些网站
  • 做网站服务销售上海做网站优化哪家好
  • 织梦做网站需要钱吗微信公众平台微网站开发
  • 天津的公司能在北京做网站备案吗wordpress 适合程序员主题