当前位置:首页 » 编程语言 » java四种线程池

java四种线程池

发布时间: 2024-09-23 04:13:49

A. 深入理解java线程

深入理解Java线程进程和线程

进程

管道以及有名管道

信号

信号量

消息队列

共享内存,比如实现分布式锁

套接字

进程是操作系统资源分配的最小单位

问题: 进程之间是如何通信的,有哪些方式

线程

线程同步: 线程之间存在一种关系,一个线程需要等待另外一个线程的消息之后才能进行,否则就需要等待

线程互斥: 对于共享资源只能线程独享,想要获取需要等待另外一个线程释放资源

volatile保证线程之间共享变量的可见性

管道输入输出流: PipedWriter、PIpedReader

join: 基于等待唤醒机制

线程是操作系统线程调度和执行的最小单位,而线程归属于进程

问题 Java线程之间如何通信的,有哪些方式

问题: 线程的同步和互斥

问题: 线程和进程之间的区别

线程更轻量级,线程的上下文切换成本比进程上下文切换成本低

进程间的通信比较复杂,线程之间的通信比较简单比如共享进程内的内存

进程是操作系统资源分配的最小单位,线程是操作系统线程调度和执行的最小单位,而线程归属于进程

问题: 四种线程同步互斥的控制方法

临界区: 通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问(在一段时间内只允许一个线程访问的资源就称为临界资源)

互斥量: 为协调共同对一个共享资源的单独访问而设计的

信号量: 为控制一个具有有限数量用户资源而设计

事件: 用来通知线程有一些事件已发生,从而启动后继任务的开始

上下文切换

问题: 什么是上下文切换

上下文切换是指CPU从一个进程或线程切换到另外一个线程或者进程,上下文切换会保存上一次的状态,以便于下一次继续执行

上下文切换只发生在内核态

上下文切换耗费时间成本比较大,尽量避免

问题: 上下文切换的步骤

暂停当前线程的处理,将当前线程的上下文保存下来,执行下一个线程的处理直到时间片用完暂停,再通过之前保存的上下文去继续执行之前线程的处理

问题: 造成CPU上下文切换的方式

进程和线程的切换

系统调用

中断机制

内核模式和用户模式

问题: 什么是内核模式和用户模式

在用户态,执行代码不能直接访问底层硬件,需要通过系统调用

在内核态,执行代码可以完全不受限制的访问底层硬件

内核模式(内核态)

用户模式(用户态)

问题: CAS操作是否涉及到用户态到内核态的切换

CAS不会涉及到用户态到内核态的切换,CAS在多核处理器下相当于在代码里插入了lock cmpxchgl指令来保证原子性,而且执行指令比上下文切换开销小,所以CAS比互斥锁性能更高

操作系统层面线程的生命周期

操作系统层面线程的生命周期

线程一开始被创建时进入初始状态,然后可以被分配给CPU时处于就绪状态,当CPU空闲的时会从就绪状态的线程中挑选一个线程去执行进入运行状态,当运行状态下的线程调用阻塞API时会进入阻塞状态等待被唤醒继续运行,当线程执行完或被异常停止处于终止状态

初始状态: 线程已经被创建,但是还不允许CPU执行

就绪状态: 线程可以分配给CPU执行

运行状态: 当CPU空闲的时候,会将它分配到一个就绪状态的线程去使用

休眠状态: 运行状态的线程调用阻塞的API时会进入阻塞状态等待被唤醒继续运行

终止状态: 线程执行结束或遇到异常

小结

Java层面线程的生命周期

Java层面线程的生命周期

NEW(初始化状态)

RUNNABLE(就绪状态 + 运行状态 = 可运行状态)

BLOCKED(阻塞状态): 只有synchronized使用

TIMED_WAITING(有时限等待状态): 调用wait()方法时指定等待的时长就会处于此状态

TERMINATED(终止状态)

问题: 概括的解释下线程的几种状态

阻塞状态就是指线程因为某种原因放弃了cpu使用权,也就是让出了cpu时间片,暂时停止运行,直到线程进入可运行状态,才有机会获得cpu时间片转到运行状态

阻塞的情况分三种

死亡(dead): 线程run、main方法执行结束、异常退出run,就代表该线程生命周期结束,死亡的线程不可再次复生

等待阻塞: 运行的线程执行wait方法,JVM会把线程放入等待队列中

同步阻塞: 运行线程在获取对象的同步锁的时候,如果锁没被释放,JVM会把该线程放入锁池中

其他阻塞: 运行的线程执行sleep、join、发送IO请求时,JVM会把线程变为阻塞状态,当sleep超时、join等待线程终止或超时、IO处理完毕时,线程重新转成可运行状态

可运行状态的线程获得了cpu时间片,执行程序代码

线程对象创建后,其他线程比如main线程调用了该对象的start方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权

新创建一个线程对象

新建(new)

可运行(runnable)

运行(running)

阻塞(block)

Java线程Java线程概述

Java线程属于内核级线程,是依赖于内核的也就是无论是用户进程中的线程还是系统进程中的线程,它们的创建、撤销、切换都是需要切换到内核态去执行

问题: ?为什么说创建Java线程的方式本质只有一种

继承Thread类实现run方法

实现Runable接口,实现run方法

实现Callable接口,实现call方法

通过线程池去创建线程: 推荐使用

虽然说创建线程的方式有以下几种

但是本质只有一种,都是通过new Thread创建线程,调用Thread.start启动线程,最终都会去调用Threead.run

问题: Java线程和Go的协程有什么区别

协程是基于线程之上但是又更加轻量级的存在,协程存在于用户态,不被操作系统内核管理

什么是协程

如果线程不用切换到内核态,开销非常小,就可以创建多个用户级别来执行任务,这样并发量特别高,所以Go天生就是和做这种大量并发的场景

问题: Java线程执行为什么不能直接调用run方法,而要调用start方法

因为run方法并不是真正的线程,只是普通对象的方法,而start方法会通过操作系统去创建线程需要切换到内核态,Java线程的创建和销毁是个比较重的操作,因为涉及到内核态切换,所以我们一般不会每一个任务分配一个线程而是选择线程复用的方式比如使用线程池

Thread的常用方法

sleep

调用sleep会让当前线程从RUNNING进入TIMED_WAITING,不会释放对象锁

其他线程可以通过interrupt方法打断正在睡眠的线程,sleep方法会抛出终端异常并且清除中断标志

睡眠结束后的线程未必立刻得到执行

sleep传入参数为0时和yield相同

yield

yield会释放CPU资源,让当前线程从RUNNING进入RUNNABLE状态,让优先级更高的线程获得执行机会,不会释放对象锁

假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比它优先级更高的线程

具体的实现依赖于操作系统的任务调度

join

可以理解为线程合并,当在一个线程调用另外一个线程的join时,当前线程阻塞等待被调用join的线程执行完毕才能继续执行,所以join的好处就是能够保证线程的执行顺序,但如果调用线程的join方法其实已经失去了并行的意义,虽然存在多个线程,但本质上是串行的,最后join底层也是采用等待唤醒机制

等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景

注意

stop

stop方法会释放对象锁,可能会造成数据不一致,因为stop方法太暴力,会强行把执行到一半的线程终止

Java线程的实现原理

线程创建和启动流程

使用new Thread()创建一个线程,然后调用start()方法进行java层面线程启动

使用本地方法start0(),去调用JVM中的JVM_StartThread()方法创建和启动

调用new JavaThread(&thread_entry,sz)进行线程的创建,并根据不同的操作系统平台调用对应os::create_thread()方法进行线程创建

新创建的线程状态是initialized,调用了sync->wait()的方法进行等待,等到被唤醒才继续执行thread->run()

调用Thread.start(native_thread)方法进行线程启动,此时将线程状态设置为RUNNABLE,接着调用os::start_thread(thread),根据不同的操作系统选择不同的线程启动方式

线程启动之后状态设置为RUNNABLE,并且唤醒第四步中等待的线程,接着执行thread->run()方法

JavaThread::run()方法会回调第一步的new Thread()中复写的run()方法

Java线程的调度机制

协同式线程调度: 线程执行时间由线程本身控制,但缺点是线程执行时间不可控制,如果一个线程有问题,可能一直阻塞在那

抢占式线程调度: 无法控制CPU时间片在哪停止,且线程的切换不由线程本身决定,Java默认就是抢占度调度

注意

轮循调度优点是简洁性,它无序记录当前所有连接的状态,所以它是一种无状态调度

抢占式调度实现相对复杂

Java线程的中断机制

Java没有提供一种安全、直接的方法来停止某个线程,而是提供了中断机制

中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理

被中断的线程拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止

API的使用

interrupt(): 将线程的中断标志位设置为true,不会停止线程

isInterrupted(): 判断当前线程的中断标志位是否为true,不会清除中断标志位

Thread.interrupted():判断当前线程的中断标志位是否为true,并清除中断标志位,重置为fasle

问题: 如何优雅的终止线程

stop会释放锁,强制终止线程,不推荐使用

可以通过while配合isInterrupted方法以及对应的结束标记来使用,注意如果代码块中有调用清除中断标记为的API时,如果使用了sleep、wait记得手动添加标记位

等待唤醒机制

等待唤醒机制可以基于wait和notify方法来实现,在一个线程内调用该线程锁对象的wait方法,线程将进入等待队列进行等待直到被唤醒

Monitor机制去提供,只作用于synchronized同步块,而且无法唤醒指定线程,而unpark可以指定线程,notify不可提前调用

notify()是随机性的,只随机唤醒一个 wait 线程

notifyAll()会唤醒所有wait线程

一般使用这种,可以唤醒指定线程,unpark提前去掉也是可以的

park/unpark

wait/notify/notifyAll

协程

协程是一种基于线程之上,但又比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行),具有对内核来说不可见的特性。这样带来的好处就是性能得到了很大的提升

问题: 协程的特点在于是一个线程执行,那和多线程比,协程有何优势

线程的切换由操作系统调度,协程由用户自己进行调度,因此减少了上下文切换,提高了效率

线程是默认stack大小是1M,而协程更轻量,接近1k.因此可以在相同的内存中开启更多的协程

不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多

问题: Java中是否存在协程

kilim ?quasar框架

注意

协程适用于被阻塞的,且需要大量并发的场景(网络io)

不适合大量计算的场景

原文:https://juejin.cn/post/7100994816468582407

B. 已拿32k小米Android高级开发offer(面试题回顾)

到现在我入职也有一段时间了,这才有空梳理一下当时的面试题。简单说下我的情况:这是一次比较平常的跳槽,不是什么逆袭大厂的剧本,只是薪资有所涨幅。

个人经历不详说,面试题对大家来说可能更有参考性,本篇先整理小米的面试题,我前后也面了很多个大厂,有空把其他几个大厂的面试题也总结一下。

Java基础肯定是少不了要问的,这轮面试Kotlin相对来说是我这些面试中问得比较多的,所以说准备面试还是要面面俱到。

我有点佩服我的记忆力了。这部分涉及到更多的 源码、原理和优化 方面的问题,Android高级开发需要具备一些什么能力大家也应该有所衡量了。

最后给大家分享一份 2246页 Android大厂高频面试题解析大全 ,基本上把我的面试内容都涵盖到了: Android、性能优化、Java、Kotlin、网络、插件化、热修复、模块化、组件化、增量更新、Gradle、图片、Flutter等。

这份资料免费提供给大家复习,文末查看领取方式,搞定Android面试这一份肯定够了。

第一章 Android相关 (源码分析、性能优化、Framework等)

第二章 性能优化 (GC原理、布局优化、绘制优化、内存优化等)

第三章 Java相关 (四种线程池、JVM、内存管理、垃圾回收、引用等)

第四章 Kotlin相关 (延迟初始化、Reified、Extension Functions、函数等)

第五章 网络相关 (HTTP 知识体系、HttpDns 原理、TCP,UDP,HTTP,SOCKET 之间的区别等)

第六章 插件化&热修复&模块化&组件化&增量更新&Gradle

第七章 图片相关 (图片库对比、LRUCache原理、图片加载原理、Glide等)

第八章 Flutter相关 (Flutter原理、Flutter Hot Reload、Flutter 动态化 探索 、Flutter Platform Channel等)

需要这份资料的朋友私信我【面试题】就可以免费领取。

希望大家都可以把握住每一次自我提升的机会,把每一步都走踏实了,涨薪升职什么的都会迎你而来。

也欢迎大家和我一起交流Android方面的事情。

C. java 线程池ThreadPoolExecutor 共同完成一个任务

线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。

为了便于跨大量上下文使用,此类提供了很多可调整的参数和扩展挂钩。但是,强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。否则,在手动配置和调整此类时,使用以下指导:

核心和最大池大小
ThreadPoolExecutor 将根据 corePoolSize(参见 getCorePoolSize())和 maximumPoolSize(参见getMaximumPoolSize())设置的边界自动调整池大小。当新任务在方法 execute(java.lang.Runnable) 中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。如果设置的 corePoolSize 和 maximumPoolSize相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

按需构造
默认情况下,即使核心线程最初只是在新任务需要时才创建和启动的,也可以使用方法 prestartCoreThread()或 prestartAllCoreThreads() 对其进行动态重写。

创建新线程
使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态,等等。如果从 newThread返回 null 时 ThreadFactory 未能创建线程,则执行程序将继续运行,但不能执行任何任务。
保持活动时间
如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(参见getKeepAliveTime(java.util.concurrent.TimeUnit))。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法 setKeepAliveTime(long, java.util.concurrent.TimeUnit) 动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS 的值在关闭前有效地从以前的终止状态禁用空闲线程。

排队
所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
A. 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
B. 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
C. 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

排队有三种通用策略:
直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集合时出现锁定。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙的情况下将新任务加入队列。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
被拒绝的任务

当 Executor 已经关闭,并且 Executor 将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法execute(java.lang.Runnable) 中提交的新任务将被拒绝。在以上两种情况下,execute 方法都将调用其RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。下面提供了四种预定义的处理程序策略:
A. 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
B. 在 ThreadPoolExecutor.CallerRunsPolicy 中,线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
C. 在 ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
D. 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)。
定义和使用其他种类的 RejectedExecutionHandler 类也是可能的,但这样做需要非常小心,尤其是当策略仅用于特定容量或排队策略时。

挂钩方法
此类提供 protected 可重写的 beforeExecute(java.lang.Thread, java.lang.Runnable) 和 afterExecute(java.lang.Runnable, java.lang.Throwable) 方法,这两种方法分别在执行每个任务之前和之后调用。它们可用于操纵执行环境;例如,重新初始化ThreadLocal、搜集统计信息或添加日志条目。此外,还可以重写方法 terminated() 来执行 Executor 完全终止后需要完成的所有特殊处理。

如果挂钩或回调方法抛出异常,则内部辅助线程将依次失败并突然终止。

队列维护
方法 getQueue() 允许出于监控和调试目的而访问工作队列。强烈反对出于其他任何目的而使用此方法。remove(java.lang.Runnable) 和 purge() 这两种方法可用于在取消大量已排队任务时帮助进行存储回收。

一、例子

创建 TestThreadPool 类:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPool {

private static int proceTaskSleepTime = 2;

private static int proceTaskMaxNumber = 10;

public static void main(String[] args) {

// 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());

for (int i = 1; i <= proceTaskMaxNumber; i++) {
try {
String task = "task@ " + i;
System.out.println("创建任务并提交到线程池中:" + task);
threadPool.execute(new ThreadPoolTask(task));

Thread.sleep(proceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
view plain
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestThreadPool {

private static int proceTaskSleepTime = 2;

private static int proceTaskMaxNumber = 10;

public static void main(String[] args) {

// 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());

for (int i = 1; i <= proceTaskMaxNumber; i++) {
try {
String task = "task@ " + i;
System.out.println("创建任务并提交到线程池中:" + task);
threadPool.execute(new ThreadPoolTask(task));

Thread.sleep(proceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

创建 ThreadPoolTask类:
view plain to clipboardprint?
import java.io.Serializable;

public class ThreadPoolTask implements Runnable, Serializable {

private Object attachData;

ThreadPoolTask(Object tasks) {
this.attachData = tasks;
}

public void run() {

System.out.println("开始执行任务:" + attachData);

attachData = null;
}

public Object getTask() {
return this.attachData;
}
}
view plain
import java.io.Serializable;

public class ThreadPoolTask implements Runnable, Serializable {

private Object attachData;

ThreadPoolTask(Object tasks) {
this.attachData = tasks;
}

public void run() {

System.out.println("开始执行任务:" + attachData);

attachData = null;
}

public Object getTask() {
return this.attachData;
}
}

执行结果:
创建任务并提交到线程池中:task@ 1
开始执行任务:task@ 1
创建任务并提交到线程池中:task@ 2
开始执行任务:task@ 2
创建任务并提交到线程池中:task@ 3
创建任务并提交到线程池中:task@ 4
开始执行任务:task@ 3
创建任务并提交到线程池中:task@ 5
开始执行任务:task@ 4
创建任务并提交到线程池中:task@ 6
创建任务并提交到线程池中:task@ 7
创建任务并提交到线程池中:task@ 8
开始执行任务:task@ 5
开始执行任务:task@ 6
创建任务并提交到线程池中:task@ 9
开始执行任务:task@ 7
创建任务并提交到线程池中:task@ 10
开始执行任务:task@ 8
开始执行任务:task@ 9
开始执行任务:task@ 10

ThreadPoolExecutor配置
一、ThreadPoolExcutor为一些Executor提供了基本的实现,这些Executor是由Executors中的工厂 newCahceThreadPool、newFixedThreadPool和newScheledThreadExecutor返回的。 ThreadPoolExecutor是一个灵活的健壮的池实现,允许各种各样的用户定制。
二、线程的创建与销毁
1、核心池大小、最大池大小和存活时间共同管理着线程的创建与销毁。
2、核心池的大小是目标的大小;线程池的实现试图维护池的大小;即使没有任务执行,池的大小也等于核心池的大小,并直到工作队列充满前,池都不会创建更多的线程。如果当前池的大小超过了核心池的大小,线程池就会终止它。
3、最大池的大小是可同时活动的线程数的上限。
4、如果一个线程已经闲置的时间超过了存活时间,它将成为一个被回收的候选者。
5、newFixedThreadPool工厂为请求的池设置了核心池的大小和最大池的大小,而且池永远不会超时
6、newCacheThreadPool工厂将最大池的大小设置为Integer.MAX_VALUE,核心池的大小设置为0,超时设置为一分钟。这样创建了无限扩大的线程池,会在需求量减少的情况下减少线程数量。
三、管理
1、 ThreadPoolExecutor允许你提供一个BlockingQueue来持有等待执行的任务。任务排队有3种基本方法:无限队列、有限队列和同步移交。
2、 newFixedThreadPool和newSingleThreadExectuor默认使用的是一个无限的 LinkedBlockingQueue。如果所有的工作者线程都处于忙碌状态,任务会在队列中等候。如果任务持续快速到达,超过了它们被执行的速度,队列也会无限制地增加。稳妥的策略是使用有限队列,比如ArrayBlockingQueue或有限的LinkedBlockingQueue以及 PriorityBlockingQueue。
3、对于庞大或无限的池,可以使用SynchronousQueue,完全绕开队列,直接将任务由生产者交给工作者线程
4、可以使用PriorityBlockingQueue通过优先级安排任务

D. java多线程有几种实现方法

  • 继承Thread类来实现多线程:

  • 当我们自定义的类继承Thread类后,该类就为一个线程类,该类为一个独立的执行单元,线程代码必须编写在run()方法中,run方法是由Thread类定义,我们自己写的线程类必须重写run方法。

    run方法中定义的代码为线程代码,但run方法不能直接调用,如果直接调用并没有开启新的线程而是将run方法交给调用的线程执行

    要开启新的线程需要调用Thread类的start()方法,该方法自动开启一个新的线程并自动执行run方法中的内容


    *java多线程的启动顺序不一定是线程执行的顺序,各个线程之间是抢占CPU资源执行的,所有有可能出现与启动顺序不一致的情况。


    CPU的调用策略:

    如何使用CPU资源是由操作系统来决定的,但操作系统只能决定CPU的使用策略不能控制实际获得CPU执行权的程序。


    线程执行有两种方式:


    1.抢占式:

    目前PC机中使用最多的一种方式,线程抢占CPU的执行权,当一个线程抢到CPU的资源后并不是一直执行到此线程执行结束,而是执行一个时间片后让出CPU资源,此时同其他线程再次抢占CPU资源获得执行权。


    2.轮循式;

    每个线程执行固定的时间片后让出CPU资源,以此循环执行每个线程执行相同的时间片后让出CPU资源交给下一个线程执行。

热点内容
linux分割行 发布:2024-11-24 22:11:02 浏览:576
获取ftp文件 发布:2024-11-24 21:54:20 浏览:520
资源平滑算法 发布:2024-11-24 21:54:18 浏览:57
vs和vc编译器哪个好使 发布:2024-11-24 21:54:07 浏览:804
爱课程适用于什么安卓系统 发布:2024-11-24 21:54:02 浏览:38
51单片机编译 发布:2024-11-24 21:50:05 浏览:366
android常用的工具类 发布:2024-11-24 21:42:25 浏览:48
用户管理源码 发布:2024-11-24 21:29:36 浏览:677
监控怎么配置路由器 发布:2024-11-24 21:29:27 浏览:455
小型编译器的实现 发布:2024-11-24 21:27:48 浏览:999