1.硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的源码实现原理
2.一文读懂ThreadLocal的原理及使用场景
3.Qt——QThread源码浅析
4.Java原理系列ScheduledThreadPoolExecutor原理用法示例源码详解
5.ThreadPoolExecutor简介&源码解析
6.InheritableThreadLocal源码剖析
硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的实现原理
深入剖析JUC线程池ThreadPoolExecutor的执行核心 早有计划详尽解读ThreadPoolExecutor的源码,因事务繁忙未能及时整理。大全在之前的源码文章中,我们曾提及Doug Lea设计的大全Executor接口,其顶层方法execute()是源码线程池扩展的基础。本文将重点关注ThreadPoolExecutor#execute()的大全尾买神器指标源码实现,结合简化示例,源码逐步解析。大全 ThreadPoolExecutor的源码核心功能包括固定的核心线程、额外的大全非核心线程、任务队列和拒绝策略。源码它的大全设计巧妙地运用了JUC同步器框架AbstractQueuedSynchronizer(AQS),以及位操作和CAS技术。源码以核心线程为例,大全设计上允许它们在任务队列满时阻塞,源码或者在超时后轮询,而非核心线程则在必要时创建。 创建ThreadPoolExecutor时,我们需要指定核心线程数、最大线程数、任务队列类型等。当核心线程和任务队列满载时,会尝试添加额外线程处理新任务。线程池的状态控制至关重要,通过整型变量ctl进行管理和状态转换,如RUNNING、SHUTDOWN、STOP等,状态控制机制包括工作线程上限数量的位操作。 接下来,我们深入剖析execute()方法。首先,方法会检查线程池状态和工作线程数量,确保在需要时添加新线程。这里涉及一个疑惑:为何需要二次检查?这主要是为了处理任务队列变化和线程池状态切换。任务提交流程中,addWorker()方法负责创建工作线程,其内部逻辑复杂,超短王者 指标源码包含线程中断和适配器Worker的创建。 Worker内部类是线程池核心,它继承自AQS,实现Runnable接口。Worker的构造和run()方法共同确保任务的执行,同时处理线程中断和生命周期的终结。getTask()方法是工作线程获取任务的关键,它会检查任务队列状态和线程池大小,确保资源的有效利用。 线程池关闭操作通过shutdown()、shutdownNow()和awaitTermination()方法实现,它们涉及线程中断、任务队列清理和状态更新等步骤,以确保线程池的有序退出。在这些方法中,可重入锁mainLock和条件变量termination起到了关键作用,保证了线程安全。 ThreadPoolExecutor还提供了钩子方法,允许开发者在特定时刻执行自定义操作。除此之外,它还包含了监控统计、任务队列操作等实用功能,每个功能的实现都是对execute()核心逻辑的扩展和优化。 总的来说,ThreadPoolExecutor的execute()方法是整个线程池的核心,它的实现原理复杂而精细。后续将陆续分析ExecutorService和ScheduledThreadPoolExecutor的源码,深入探讨线程池的扩展和调度机制。敬请关注,期待下文的详细解析。一文读懂ThreadLocal的原理及使用场景
ThreadLocal 类是用来提供线程内部的局部变量,即线程本地变量。这种变量在多线程环境下访问(通过get和set方法访问)时能够保证各个线程的变量相对独立于其他线程内的变量,不同线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度。
ThreadLocal 表示线程的“本地变量”,即每个线程都拥有该变量副本,同步cm源码编译达到人手一份的效果,各用各的,这样就可以避免共享资源的竞争。
在高并发中会存在多个线程同时修改一个共享变量的场景,这就可能会出现线性安全问题。为了解决线性安全问题,可以通过加锁来实现,例如使用synchronized 或者Lock。但是加锁的方式可能会导致系统变慢。另外一种方式,可以使用ThreadLocal类访问共享变量,这样会在每个线程的本地,都保存一份共享变量的拷贝副本。这是一种“空间换时间”的方案,虽然会让内存占用大很多,但是由于不需要同步也就减少了线程可能存在的阻塞等待,从而提高时间效率。
接下来就让我们学习 ThreadLocal 的几个核心方法,来了解ThreadLocal 的实现原理。
set() 方法设置当前线程中 ThreadLocal 变量的值,该方法的源码为:通过源码我们知道 value 是存放在 ThreadLocalMap 里的,数据 value 是存放在 ThreadLocalMap 这个容器中的,并且是以当前 ThreadLocal 实例为 key 的。
ThreadLocalMap 是怎样来的?源码很清楚,是通过getMap(t)进行获取:该方法直接返回当前线程对象 t 的一个成员变量 ThreadLocals:也就是说ThreadLocalMap 的引用是作为 Thread 的一个成员变量的,被 Thread 进行维护的。
总结一下 set 方法:通过当前线程对象 thread 获取该 thread 所维护的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则以 ThreadLocal 实例为 key,值为 value 的键值对存入 ThreadLocalMap,若 ThreadLocalMap 为 null 的话,就新建 ThreadLocalMap,然后再以 ThreadLocal 为键,值为 value 的键值对存入即可。
get() 方法是获取当前线程中 ThreadLocal 变量的值,代码逻辑请看注释,另外,修改电脑mac源码看下 setInitialValue 主要做了些什么事情?这段方法的逻辑和 set 方法几乎一致,关注的是 initialValue 方法:这个方法是 protected 修饰的,也就是说继承 ThreadLocal 的子类可重写该方法,实现赋值为其他的初始值。
总结一下 get 方法:通过当前线程 thread 实例获取到它所维护的 ThreadLocalMap,然后以当前 ThreadLocal 实例为 key 获取该 map 中的键值对(Entry),如果 Entry 不为 null 则返回 Entry 的 value。如果获取 ThreadLocalMap 为 null 或者 Entry 为 null 的话,就以当前 ThreadLocal 为 Key,value 为 null 存入 map 后,并返回 null。
remove() 方法实现了如何删数据的操作。删除数据当然是从 map 中删除数据,先获取与当前线程相关联的 ThreadLocalMap,然后从 map 中删除该 ThreadLocal 实例为 key 的键值对即可。
从上面的分析我们已经知道,数据其实都放在了 ThreadLocalMap 中,ThreadLocal 的 get、set 和 remove 方法实际上都是通过 ThreadLocalMap 的 getEntry、set 和 remove 方法实现的。ThreadLocalMap 是 ThreadLocal 一个静态内部类,内部维护了一个数组(Entry 类型的 table 数组),Entry 是一个以 ThreadLocal 为 key,Object 为 value 的键值对,这里的ThreadLocal 是弱引用。每个线程实例中都可以通过 ThreadLocals 获取到 ThreadLocalMap,而 ThreadLocalMap 实际上就是一个以 ThreadLocal 实例为 key,任意对象为 value 的 Entry 数组。当我们为 ThreadLocal 变量赋值时,实际上就是以当前 ThreadLocal 实例为 key,值为 value 的 Entry 往这个 ThreadLocalMap 中存放。需要注意的是,Entry 中的 key 是弱引用,当 ThreadLocal 外部强引用被置为 null(ThreadLocalInstance=null)时,系统 GC 的时候,根据可达性分析,这个 ThreadLocal 实例就没有任何一条链路能够引用到它,java权限设计源码此时 ThreadLocal 势必会被回收,这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry,如果没有办法访问这些 key 为 null 的 Entry 的 value,如果当前线程再迟迟不结束的话,这些 key 为 null 的 Entry 的 value 就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value 永远无法回收,造成内存泄漏。
当然,如果当前 thread 运行结束,ThreadLocal、ThreadLocalMap、Entry 没有引用链可达,在垃圾回收的时候都会被系统回收。在实际开发中,会使用线程池去维护线程的创建和复用,比如固定大小的线程池,线程为了复用是不会主动结束的。
本文主要讲解了ThreadLocal的作用及基本用法,以及ThreadLocal的实现原理和基础方法。线上环境中,ThreadLocal还有可能引起内存泄漏,这方面内容我们后续接着讲。
本文由 mdnice 多平台发布
Qt——QThread源码浅析
在探索Qt的多线程处理中,QThread类的实现源码历经变迁。在Qt4.0.1和Qt5.6.2版本中,尽管QThread类的声明相似,但run()函数的实现有所不同。从Qt4.4开始,QThread不再是抽象类,这标志着一些关键调整。
QThread::start()函数在不同版本中的核心代码保持基本一致,其中Q_D()宏定义是一个预处理宏,用于获取QThread的私有数据。_beginthreadex()函数则是创建线程的核心,调用QThreadPrivate::start(this),即执行run()函数并发出started()信号。
QThread::run()函数在Qt4.4后的版本中,不再强制要求重写,而是可以通过start启动事件循环。在Qt5.6.2版本中,run函数的定义更灵活,可以根据需要进行操作。
关于线程停止,QThread提供了quit()、exit()和terminate()三种方式。quit()和exit(0)等效,用于事件循环中停止线程,而terminate()则立即终止线程,但不推荐使用,因为它可能引发不稳定行为。
总结起来,QThread的核心功能包括线程的创建、run函数的执行以及线程的结束控制。从Qt4.4版本开始,QThread的使用变得更加灵活,可以根据需要选择是否重写run函数,以及如何正确地停止线程。不同版本间的细微差别需要开发者注意,以确保代码的兼容性和稳定性。
Java原理系列ScheduledThreadPoolExecutor原理用法示例源码详解
ScheduledThreadPoolExecutor是Java中实现定时任务与周期性执行任务的高效工具。它继承自ThreadPoolExecutor类,能够提供比常规Timer类更强大的灵活性与功能,特别是在需要多个工作线程或有特殊调度需求的场景下。
该类主要功能包含但不限于提交在指定延迟后执行的任务,以及按照固定间隔周期执行的任务。它实现了ScheduledExecutorService接口,进而提供了丰富的API以实现任务的调度与管理。其中包括now()、getDelay()、compareTo()等方法,帮助开发者更精确地处理任务调度与延迟。
在实际应用中,ScheduledThreadPoolExecutor的使用案例广泛。比如,初始化一个ScheduledThreadPoolExecutor实例,设置核心线程数,从而为定时任务提供资源保障。提交延迟任务,例如在5秒后执行特定操作,并输出相关信息。此外,提交周期性任务,如每隔2秒执行一次特定操作,用于实时监控或数据更新。最后,通过调用shutdown()与shutdownNow()方法来关闭执行器并等待所有任务完成,确保系统资源的合理释放与任务的有序结束。
总的来说,ScheduledThreadPoolExecutor在处理需要精确时间控制的任务时展现出了强大的功能与灵活性,是Java开发者在实现定时与周期性任务时的首选工具。
ThreadPoolExecutor简介&源码解析
线程池是通过池化管理线程的高效工具,尤其在多核CPU时代,利用线程池进行并行处理任务有助于提升服务器性能。ThreadPoolExecutor是线程池的具体实现,它负责线程管理和任务管理,以及处理任务拒绝策略。这个类提供了多种功能,如通过Executors工厂方法配置,执行Runnable和Callable任务,维护任务队列,统计任务完成情况等。
创建线程池需要考虑关键参数,如核心线程数(任务开始执行时立即创建),最大线程数(任务过多时限制新线程生成),线程存活时间,任务队列大小,线程工厂以及拒绝策略。JDK提供了四种拒绝策略,如默认的AbortPolicy,当资源饱和时抛出异常。此外,线程池还提供了beforeExecute和afterExecute钩子函数,用于在任务执行前后执行自定义操作。
当任务提交到线程池时,会经历一系列处理流程,包括任务的执行和线程池状态的管理。例如,如果任务队列和线程池满,会根据拒绝策略处理新任务。使用线程池时,需注意线程池容量与状态的计算,以及线程池工作线程的动态调整。
示例中,自定义线程池并重写钩子函数,创建任务后向线程池提交,可以看到线程池如何根据配置动态调整资源。但要注意,如果任务过多且无法处理,可能会抛出异常。源码分析中,submit方法实际上是调用execute,而execute内部包含Worker类和runWorker方法的逻辑,包括任务的获取和执行。
线程池的容量上限并非Integer.MAX_VALUE,而是由ctl变量的低位决定。 Doug Lea的工具函数简化了ctl的操作,使得计算线程池状态和工作线程数更加便捷。通过深入了解ThreadPoolExecutor,开发者可以更有效地利用线程池提高应用性能。
InheritableThreadLocal源码剖析
InheritableThreadLocal是Java中用于在多线程环境共享数据的工具,它允许子线程继承父线程的值,从而避免了线程间数据同步的复杂性。与ThreadLocal不同,InheritableThreadLocal实现了数据的继承机制,确保了数据在父线程到子线程间的顺利传递。这使得在使用线程池或其他线程管理技术时,应用程序能够保持数据的一致性和完整性。
InheritableThreadLocal提供了一种从父线程到子线程的数据传递方式,它通过在Thread类中引入了inheritableThreadLocals字段来实现这一功能。这一字段是一个ThreadLocalMap类型的对象,专门用于存储InheritableThreadLocal实例。这意味着当创建子线程时,它会自动接收并继承父线程的值。
实现这一特性,InheritableThreadLocal主要通过三个关键方法:set、get、remove。它们与ThreadLocal的同名方法相似,但操作的内部数据结构有所不同。InheritableThreadLocal的set、get、remove方法会通过获取inheritableThreadLocals字段中的ThreadLocalMap对象来进行操作,而ThreadLocal则通过操作threadLocals字段。
为了验证InheritableThreadLocal的继承机制,可以通过在父线程中设置InheritableThreadLocal的值,然后在子线程中尝试获取该值来观察结果。实验证明,子线程能够成功获取到父线程设置的值,证明了InheritableThreadLocal的继承功能。
在使用InheritableThreadLocal时,需要注意的是它的内存管理。一旦线程创建了InheritableThreadLocal实例,它会一直保留在所有后代线程中,直到显式调用remove方法或线程结束。因此,在资源管理和内存控制上,开发者需要特别注意,以防止潜在的内存泄漏问题。
总之,InheritableThreadLocal通过在Thread类中引入专门的数据结构和方法来实现其独特的继承机制,简化了多线程编程中数据共享和管理的复杂性。然而,其使用需要谨慎,以避免不必要的内存占用和潜在的内存泄漏风险。
ãPocoç¬è®°ã线ç¨Thread
PocoçThreadæ¯å¯¹æ ååºstd::threadçå°è£ ï¼åæ¶å®ç±»ä¼¼Javaä¸æ ·ï¼æä¾äºRunnableæ¥å£ãæ以使ç¨ä¸æ¯å¯¹æ Javaçãä¸æ ååºä¸åçæ¯ï¼Poco::Threadå建åè¿è¡æ¶ç¸å离çãè¿ä¸ç¹æ ååºè®¾è®¡ç¡®å®ä¸å¤ªå好ãä¾å¦ä¸é¢ä¾åã
åæ ·çä¾å
ç±ä¸é¢å¯è§ï¼ä½¿ç¨åºæ¬è·Java类似ãå建ä¸è¿è¡ä¹å离äºã
çä¸ä¸ä¸»è¦çè¿è¡æ¥å£ï¼æèªPoco1.9æºç
æºç æ件主è¦å å«
1.Thread.h/Thread.cpp
æä¾å¤é¨è°ç¨æ¥å£
å¨Thread.cppä¸å®ä¹äºä¸¤ç§Holder, RunnableHolderåCallableHolderãHolderææ¯æ¯Pocoæ¡æ¶ä¸ç»å¸¸ç¨å°çï¼æ¯å¯¹æä¸ç§ç±»å对象çæéå è£ ã
Runnable为线ç¨è¿è¡ç±»çåºç±»ï¼
Callable为带ä¸ä¸ªåæ°çæ¹æ³
2.Thread_POSIX.h/Thread_POSIX.cpp
3.Thread_VX.h/Thread_VX.cpp
4.Thread_WIN.h/Thread_WIN.cpp
5.Thread_WINCE.h/Thread_WINCE.cpp
è¿å 个æ件ï¼æ¯ä¸ªæ件ä¸é½å®ä¹äºThreadImplï¼ç¨äºä¸åå¹³å°ä¸çå ·ä½å®ç°ï¼Threadç§æ继æ¿ThreadImpï¼ThreadImpç¨äºåªä¸ä¸ªæ件ç±ç¼è¯å®å³å®ã
顺便说ä¸ä¸POSIXç³»ç»ä¸çå®ç°ãå 为使ç¨çæ¯c++ï¼å½æ¶æ²¡æthreadç±»ï¼æ以ææçå®ç°é½æ¯ä½¿ç¨pthreadåºæ¥å®ç°çãå ·ä½ç使ç¨è¯·åèpthreadææ¯ææ¡£ã
6.ThreadLocal.h/ThreadLocal.cpp
ThreadLocalä¸å®ä¹äºä¸ä¸ªç±»ï¼ TLSAbstractSlotç±»ï¼ TLSSlotç±»ï¼ ThreadLocalStorageç±»
TLSAbstractSlotæ¯åºç±»ï¼TLSSlotæ¯æ¨¡æ¿ç±»ï¼éè¿æ¨¡æ¿ææ¯å 裹äºå ·ä½çç±»åãThreadLocalStorageæ¯ç¨äºçº¿ç¨åå¨ï¼å ·ä½æ¯éè¿ä¸ä¸ªmapæ¥å®ç°ã
å 为1.9使ç¨çæ¯c++ï¼è¿æ²¡æå¼ç¨local_threadå ³é®åï¼æ以è¿éæ¯éè¿è¿ç§æ¹å¼å®ç°ã
ThreadLocalStorageå®ä¹å¦ä¸
é£ä¹Poco::Threadçtlsæ¯å¦ä½å®ä¹çï¼
æºç æ件æ¯è¾å°ï¼ä¸»è¦å¦ä¸æ件
1.Thread.h/Thread.cpp
2.Thread_STD.h/Thread_POSIX.cpp/Thread_VX.cpp/Thread_WIN.cpp
Thread.h 主è¦å¯¹å®ç°ç±»ThreadImpçå è£ ï¼å¹¶å®ä¹äºå¯¹å¤æ¥å£ã
Thread_STD.hå®ä¹äºå é¨å®ç°,主è¦æä¾äºThreadImpç±»
Thread_POSIX.cpp/Thread_VX.cpp/Thread_WIN.cppåå«å®ä¹ä¸åå¹³å°ä¸çå ¼å®¹å®ç°
å¨Thread_STD.hä¸å®ä¹äºå 个éè¦ç±»å
å¨Thread.cppä¸å¢å äºä¸¤ç§
private修饰çThreadDataï¼å®ä¹äºçº¿ç¨å é¨æ°æ®ã 1.9ä¸æºç åå«å®ä¹å¨å个平å°å®ç°ç±»ä¸ï¼è¿éæ½ç¦»åºæ¥å®ä¹å¨Thread.cppä¸ãè¾ä¹åçå®ä¹ï¼è¿éé¢å¤çæ¯æ°å¢äºstd::threadæéãå 为ç´æ¥å¼ç¨äºc++ä¸çthreadï¼æäºå®ç°ç´æ¥åå©äºå®ã
详解java Thread中的join方法
在Java编程中,Thread类的join()方法发挥着关键作用。当需要控制线程执行顺序时,它能让调用线程暂停,直至被调用的线程完成。在主线程(如main())中,join()尤其有用,它会阻止主线程直到目标线程结束,例如:
当调用t1.join()时,main()线程会被暂停,直到t1线程完全执行完毕,然后main()线程才会继续执行。
join()方法的工作原理主要依赖于Java内存模型中的同步机制。通过查看Thread类的源码,我们发现join()实际上调用了wait()方法,使调用线程进入等待状态,直到目标线程结束。由于wait()方法前有synchronized修饰,这意味着主线程(t1线程的持有者)会在一个锁定的上下文中等待,如下所示:
代码等效于:synchronized(this) { wait(); },使得主线程进入等待队列,直到t1线程结束。
然而,wait()方法本身并不会唤醒主线程,唤醒过程隐藏在Java虚拟机(JVM)的底层。当t1线程执行完毕,JVM会自动调用lock.notify_all()方法,将主线程从等待队列中唤醒。
总结起来,join()方法的使用需要注意以下两点:
1. 它让调用线程暂停,直到目标线程结束。
2. 唤醒机制由JVM内部的notify_all()方法控制,确保线程按照预期顺序执行。
理解这些原理,能帮助你更有效地管理和控制Java线程。