CompletableFuture.allOf() and Executor.shutdown()

这是两块内容 CompletableFuture.allOf() allOf()会非阻塞等待所有的CompletableFuture都完成,无论这些task中是否有抛出异常,也就是说即便其中有某些抛出了异常,allOf()的CompletableFuture调用join()阻塞等待时也不会立即抛出异常,而是等所有需等待的都完成,才会抛出异常,这也是allOf的含义之一 Executor.shutdown() 当ThreadPool调用shutdown()时,调用后只是不再接收新的Task,已提交的task会继续执行无任何影响,并且对该方法的调用并不会阻塞等待创建的thread结束。但如果main thread结束,那应该都结束。 另外就是当使用ThreadPool来run CompletableFuture时,若每次都new ThreadPool,则InheritableThreadLocal会正常,若使用默认或预定义的ThreadPool,则InheritableThreadLocal会失效(因为其只会在Create Thread时传递,而ThreadPool中的Thread是share的,而非每次都Create New)

InheritableThreadLocal and ThreadPools

https://stackoverflow.com/questions/7296623/inheritablethreadlocal-and-thread-pools 当ThreadLocal,尤其是InheritableThreadLocal和ThreadPool一起使用时,可能出现问题, 因为ThreadPool会复用线程,而子线程保存的一直是首次被call时,父线程的TL快照,当被复用时,ITL中的值是不会变的。所以即便在父线程中更新了TL(父线程大多是新的线程),被复用的子线程中的ITL也不会变。这会带来很多潜在的问题。一个推荐的解决方式是使用TL然后自行封装向子线程的传递。这个在link上有 https://github.com/lWoHvYe/unicorn/commit/a839e5cbac88d0bffb7fffc7a1b61a0b9be82386 在ITL的注释中,已经很明确了,是create时 when a child thread is created, the child receives initial values for all inheritable thread-local variables for which the parent has values.

Java – 可重入锁ReentrantLock实现原理

注:不同版本的JDK,此部分源码存在差异,当下17版本与本文1.8版本差异较大 在实现层面除了依赖于CAS(compareAndSet)方法之外,同时依赖于类LockSupport中的一些方法。 一、LockSupport 类 LockSupport 位于包 java.util.concurrent.locks ,其基本方法有 public static void park()public static void parkNanos(long nanos)public static void parkUntil(long deadline)public static void unpark(Thread thread) 其中park方法会使得当前线程放弃CPU,进入等待(WAITING)状态,操作系统不会再对其进行调度。直到其他线程对它调用了unpark方法,其中unpark方法使得参数指定的线程恢复可运行状态: public class LockSupportTest {   public static void main(String[] args) throws InterruptedException {       Thread t = new Thread (){           public void run(){ […]

JUC整理笔记-VarHandle

前面整理完 Unsafe ,不得不去了解下 java.lang.invoke.Varhandle 。如前面文章所说, Unsafe 是不建议开发者直接使用的,因为 Unsafe 所操作的并不属于Java标准,会容易带来一些安全性的问题。JDK9 之后,官方推荐使用 java.lang.invoke.Varhandle 来替代 Unsafe 大部分功能,对比 Unsafe ,Varhandle 有着相似的功能,但会更加安全,并且,在并发方面也提高了不少性能。以下有用到MethodHandle,可以先去了解一下。 简介 Varhandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。 在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile 的读/写访问以及 CAS (compare-and-set)访问。简单来说 Variable 就是对这些变量进行绑定,通过 Varhandle 直接对这些变量进行操作。 实例 目标实体类 public class Demo {   public int publicVar = 1;   protected int protectedVar = 2;   private int privateVar = 3;   public int[] arrayData […]

JUC整理笔记-Unsafe

转:JFound JUC(java.util.concurrent)的开始,可以说是从Unsafe类开始。 Unsafe 简介 Unsafe在sun.misc 下,顾名思义,这是一个不安全的类,因为Unsafe类所操作的并不属于Java标准,Java的一系列内存操作都是交给jvm的,而Unsafe类却能有像C语言的指针一样直接操作内存的能力,同时也会带来了指针的问题。过度使用Unsafe类的话,会使出错率变得更大,因此官方才命名为Unsafe,并且不建议使用,连注释的没有。 而为了安全使用Unsafe,Unsafe类只允许jdk自带的类使用,从下面的代码中可以看出 public static Unsafe getUnsafe() {     Class<?> caller = Reflection.getCallerClass();     if (!VM.isSystemDomainLoader(caller.getClassLoader()))         throw new SecurityException(“Unsafe”);     return theUnsafe; } 如果当前Class是非系统加载的(也就是caller.getClassLoader()不为空),直接抛出SecurityException 。 在java9之后,又出现了一个jdk.internal.misc.Unsafe类,其功能与sun.misc.Unsafe类是一样的,唯一不一样的是在 getSafe() 的时候,jdk.internal.misc.Unsafe是没有做校验的,但是jdk包下的代码,应用开发时是不能直接调用的,而且在java9之后,两个Unsafe类都有充足的注释。 获取Unsafe Unsafe类里有这样的一个field。 private static final Unsafe theUnsafe = new Unsafe(); 也就是说虽然不能直接拿到Unsafe对象,但是还是可以通过反射去获取的。 private static Unsafe getUnsafe() throws […]

CompletableFuture各种用法(转)

这篇文章介绍 Java 8 的 CompletionStage API和它的标准库的实现 CompletableFuture。API通过例子的方式演示了它的行为,每个例子演示一到两个行为。 既然CompletableFuture类实现了CompletionStage接口,首先我们需要理解这个接口的契约。它代表了一个特定的计算的阶段,可以同步或者异步的被完成。你可以把它看成一个计算流水线上的一个单元,最终会产生一个最终结果,这意味着几个CompletionStage可以串联起来,一个完成的阶段可以触发下一阶段的执行,接着触发下一次,接着…… 除了实现CompletionStage接口, CompletableFuture也实现了future接口, 代表一个未完成的异步事件。CompletableFuture提供了方法,能够显式地完成这个future,所以它叫CompletableFuture。 创建一个完成的CompletableFuture 最简单的例子就是使用一个预定义的结果创建一个完成的CompletableFuture,通常我们会在计算的开始阶段使用它。 getNow(null)方法在future完成的情况下会返回结果,就比如上面这个例子,否则返回null (传入的参数)。 运行一个简单的异步阶段 这个例子创建一个一个异步执行的阶段: 通过这个例子可以学到两件事情: CompletableFuture的方法如果以Async结尾,它会异步的执行(没有指定executor的情况下), 异步执行通过ForkJoinPool实现, 它使用守护线程去执行任务。注意这是CompletableFuture的特性, 其它CompletionStage可以override这个默认的行为。 在前一个阶段上应用函数 下面这个例子使用前面 #1 的完成的CompletableFuture, #1返回结果为字符串message,然后应用一个函数把它变成大写字母。 注意thenApply方法名称代表的行为。 then意味着这个阶段的动作发生当前的阶段正常完成之后。本例中,当前节点完成,返回字符串message。 Apply意味着返回的阶段将会对结果前一阶段的结果应用一个函数。 函数的执行会被阻塞,这意味着getNow()只有转大写操作被完成后才返回。 在前一个阶段上异步应用函数 通过调用异步方法(方法后边加Async后缀),串联起来的CompletableFuture可以异步地执行(使用ForkJoinPool.commonPool())。 getNow在cf.join()之前,所以与转大写操作没有明确的先后关系 使用定制的Executor在前一个阶段上异步应用函数 异步方法一个非常有用的特性就是能够提供一个Executor来异步地执行CompletableFuture。这个例子演示了如何使用一个固定大小的线程池来应用大写函数。 消费前一阶段的结果 如果下一阶段接收了当前阶段的结果,但是在计算的时候不需要返回值(它的返回类型是void), 那么它可以不应用一个函数,而是一个消费者, 调用方法也变成了thenAccept: 本例中消费者同步地执行,所以我们不需要在CompletableFuture调用join方法。 异步地消费前一阶段的结果 同样,可以使用thenAcceptAsync方法, 串联的CompletableFuture可以异步地执行。 完成计算异常 现在我们来看一下异步操作如何显式地返回异常,用来指示计算失败。我们简化这个例子,操作处理一个字符串,把它转换成答谢,我们模拟延迟一秒。 我们使用thenApplyAsync(Function, Executor)方法,第一个参数传入大写函数, executor是一个delayed executor,在执行前会延迟一秒。 让我们看一下细节。 首先我们创建了一个CompletableFuture, 完成后返回一个字符串message,接着我们调用thenApplyAsync方法,它返回一个CompletableFuture。这个方法在第一个函数完成后,异步地应用转大写字母函数。 这个例子还演示了如何通过delayedExecutor(timeout, timeUnit)延迟执行一个异步任务。 […]

Java线程池实现原理

转:https://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651751537&idx=1&sn=c50a434302cc06797828782970da190e&chksm=bd125d3c8a65d42aaf58999c89b6a4749f092441335f3c96067d2d361b9af69ad4ff1b73504c&scene=21#wechat_redirect 主体是了解原理。及关于动态线程池的一个思路。并且说明线程池的部分参数是支持动态设置的。 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流。使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器。J.U.C提供的线程池ThreadPoolExecutor类,帮助开发人员管理线程并方便地执行并行任务。了解并合理使用线程池,是一个开发人员必修的基本功。 本文开篇简述线程池概念和用途,接着结合线程池的源码,帮助读者领略线程池的设计思路,最后回归实践,通过案例讲述使用线程池遇到的问题,并给出了一种动态化线程池解决方案。 一、写在前面 1.1 线程池是什么 线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。 线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。 而本文描述线程池是JDK中提供的ThreadPoolExecutor类。 当然,使用线程池可以带来一系列好处: 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。 提高响应速度:任务到达时,无需等待线程创建即可立即执行。 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。 1.2 线程池解决的问题是什么 线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题: 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。 系统无法合理管理内部的资源分布,会降低系统的稳定性。 为解决资源分配这个问题,线程池采用了“池化”(Pooling)思想。池化,顾名思义,是为了最大化收益并最小化风险,而将资源统一在一起管理的一种思想。 Pooling is the grouping together of resources (assets, equipment, personnel, effort, etc.) for the purposes of maximizing advantage or minimizing risk to the users. The term is used in finance, computing and […]

多线程面试题-2021

Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多、越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的。这篇文章主要是对多线程的问题进行总结的,因此罗列了108个多线程的问题。 这些多线程的问题来源于各大网站,可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过,但是本文写作的重心就是所有的问题都会按照自己的理解回答一遍,不会去看网上的答案,因此可能有些问题讲的不对,能指正的希望大家不吝指教。 # 106个问题汇总 1、多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓”知其然知其所以然”,”会用”只是”知其然”,”为什么用”才是”知其所以然”,只有达到”知其然知其所以然”的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问题的看法: (1)发挥多核CPU的优势 随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4核、8核甚至16核的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上所谓的”多线程”那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程”同时”运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。 (2)防止阻塞 从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。 (3)便于建模 这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。 2、创建线程的方式 比较常见的一个问题了,一般就是两种: (1)继承Thread类 (2)实现Runnable接口 至于哪个好,不用说肯定是后者好,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,面向接口编程也是设计模式6大原则的核心。 3、start()方法和run()方法的区别 只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。 4、Runnable接口和Callable接口的区别 有点深的问题了,也看出一个Java程序员学习知识的广度。 Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。 这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。 5、CyclicBarrier和CountDownLatch的区别 两个看上去有点像的类,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于: (1)CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行 (2)CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务 (3)CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了 6、volatile关键字的作用 一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了,可以参见第31点,volatile关键字的作用主要有两个: (1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据 (2)代码底层执行不像我们看到的高级语言—-Java程序这么简单,它的执行是Java代码–>字节码–>根据字节码执行对应的C/C++代码–>C/C++代码被编译成汇编语言–>和硬件电路交互,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率 从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger。 7、什么是线程安全 又是一个理论的问题,各式各样的答案有很多,我给出一个个人认为解释地最好的:如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。 这个问题有值得一提的地方,就是线程安全也是有几个级别的: (1)不可变 像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用 (2)绝对线程安全 不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet (3)相对线程安全 相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。 (4)线程非安全 这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类 8、Java中如何获取到线程dump文件 死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步: (1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java (2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid […]

Java多线程通信工具类

转自:深入浅出Java多线程http://concurrent.redspider.group/article/03/17.html JDK中提供了一些工具类以供开发者使用。这样的话我们在遇到一些常见的应用场景时就可以使用这些工具类,而不用自己再重复造轮子了。 它们都在java.util.concurrent包下。先总体概括一下都有哪些工具类,它们有什么作用,然后再分别介绍它们的主要使用方法和原理。 类 作用 Semaphore 限制线程的数量 Exchanger 两个线程交换数据 CountDownLatch 线程等待直到计数器减为0时开始工作 CyclicBarrier 作用跟CountDownLatch类似,但是可以重复使用 Phaser 增强的CyclicBarrier 下面分别介绍这几个类。 17.1 Semaphore 17.1.1 Semaphore介绍 Semaphore翻译过来是信号的意思。顾名思义,这个工具类提供的功能就是多个线程彼此“打信号”。而这个“信号”是一个int类型的数据,也可以看成是一种“资源”。 可以在构造函数中传入初始资源总数,以及是否使用“公平”的同步器。默认情况下,是非公平的。 // 默认情况下使用非公平public Semaphore(int permits) {    sync = new NonfairSync(permits);}​public Semaphore(int permits, boolean fair) {    sync = fair ? new FairSync(permits) : new NonfairSync(permits);} 最主要的方法是acquire方法和release方法。acquire()方法会申请一个permit,而release方法会释放一个permit。当然,你也可以申请多个acquire(int permits)或者释放多个release(int permits)。 每次acquire,permits就会减少一个或者多个。如果减少到了0,再有其他线程来acquire,那就要阻塞这个线程直到有其它线程release permit为止。 17.1.2 Semaphore案例 Semaphore往往用于资源有限的场景中,去限制线程的数量。举个例子,我想限制同时只能有3个线程在工作: […]

lWoHvYe 无悔,专一