juc?߳?Դ??
一:简述如果我们想要生成一个随机数,通常会使用Random类。线a线但是程源程源在并发情况下Random生成随机数的性能并不是很理想,今天给大家介绍一下JUC包中的码j码欢乐海岸指标公式源码用于生成随机数的类--ThreadLocalRandom.(本文基于JDK1.8)
二:Random的性能差在哪里
Random随机数生成是和种子seed有关,而为了保证线程安全性,线a线Random通过CAS机制来保证线程安全性。程源程源从next()方法中我们可以发现seed是码j码通过自旋锁和CAS来进行修改值的。如果在高并发的线a线场景下,那么可能会导致CAS不断失败,程源程源从而导致不断自旋,码j码这样就可能会导致服务器CPU过高。线a线
protected?程源程源int?next(int?bits)?{ long?oldseed,?nextseed;AtomicLong?seed?=?this.seed;do?{ oldseed?=?seed.get();nextseed?=?(oldseed?*?multiplier?+?addend)?&?mask;}?while?(!seed.compareAndSet(oldseed,?nextseed));return?(int)(nextseed?>>>?(?-?bits));}复制代码三:ThreadLocalRandom的简单使用
使用的方法很简单,通过ThreadLocalRandom.current()获取到ThreadLocalRandom实例,码j码然后通过nextInt(),nextLong()等方法获取一个随机数。
代码:
@Testvoid?test()?throws?InterruptedException?{ new?Thread(()->{ ThreadLocalRandom?random?=?ThreadLocalRandom.current();System.out.println(random.nextInt());}).start();new?Thread(()->{ ThreadLocalRandom?random?=?ThreadLocalRandom.current();System.out.println(random.nextInt());}).start();Thread.sleep();}复制代码运行结果:
四:为什么ThreadLocalRandom能在保证线程安全的情况下还能有不错的性能
我们可以看一下ThreadLocalRandom的代码实现。
首先我们很容易看出这是一个饿汉式的单例
/**?Constructor?used?only?for?static?singleton?*/private?ThreadLocalRandom()?{ initialized?=?true;?//?false?during?super()?call}/**?The?common?ThreadLocalRandom?*/static?final?ThreadLocalRandom?instance?=?new?ThreadLocalRandom();复制代码我们可以看到PROBE成员变量代表的是Thread类的threadLocalRandomProbe属性的内存偏移量,SEED成员变量代表的是Thread类的threadLocalRandomSeed属性的内存偏移量,SECONDARY成员变量代表的是Thread类的threadLocalRandomSecondarySeed属性的内存偏移量。
//?Unsafe?mechanicsprivate?static?final?sun.misc.Unsafe?UNSAFE;private?static?final?long?SEED;private?static?final?long?PROBE;private?static?final?long?SECONDARY;static?{ try?{ UNSAFE?=?sun.misc.Unsafe.getUnsafe();Class<?>?tk?=?Thread.class;SEED?=?UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));PROBE?=?UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));SECONDARY?=?UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));}?catch?(Exception?e)?{ throw?new?Error(e);}}复制代码可以看到Thread类中确实有这三个属性
Thread类:
@sun.misc.Contended("tlr")//当前Thread的随机种子?默认值是0long?threadLocalRandomSeed;/**?Probe?hash?value;?nonzero?if?threadLocalRandomSeed?initialized?*/@sun.misc.Contended("tlr")//用来标志当前Thread的threadLocalRandomSeed是否进行了初始化?0代表没有,非0代表已经初始化?默认值是0int?threadLocalRandomProbe;/**?Secondary?seed?isolated?from?public?ThreadLocalRandom?sequence?*/@sun.misc.Contended("tlr")//当前Thread的二级随机种子?默认值是0int?threadLocalRandomSecondarySeed;复制代码接下来我们看ThreadLocalRandom.current()方法。
ThreadLocalRandom.current()
ThreadLocalRandom.current()的作用主要是初始化随机种子,并且返回ThreadLocalRandom的实例。
首先通过UNSAFE类获取当前线程的Thread对象的threadLocalRandomProbe属性,看随机种子是否已经初始化。没有初始化,那么调用localInit()方法进行初始化
public?static?ThreadLocalRandom?current()?{ //?获取当前线程的if?(UNSAFE.getInt(Thread.currentThread(),?PROBE)?==?0)localInit();return?instance;}复制代码localInit()
localInit()方法的作用就是初始化随机种子,可以看到代码很简单,就是通过UNSAFE类对当前Thread的threadLocalRandomProbe属性和threadLocalRandomSeed属性进行一个赋值。
static?final?void?localInit()?{ int?p?=?probeGenerator.addAndGet(PROBE_INCREMENT);int?probe?=?(p?==?0)?1?:?p;?//?skip?0long?seed?=?mix(seeder.getAndAdd(SEEDER_INCREMENT));Thread?t?=?Thread.currentThread();UNSAFE.putLong(t,?SEED,?seed);UNSAFE.putInt(t,?PROBE,?probe);}复制代码接下来以nextInt()方法为例,看ThreadLocalRandom是如何生成到随机数的。我们可以看出随机数正是通过nextSeed()方法获取到随机种子,然后通过随机种子而生成。所以重点看nextSeed()方法是如何获取到随机种子的。
public?int?nextInt(int?bound)?{ if?(bound?<=?0)throw?new?IllegalArgumentException(BadBound);int?r?=?mix(nextSeed());int?m?=?bound?-?1;if?((bound?&?m)?==?0)?//?power?of?twor?&=?m;else?{ ?//?reject?over-represented?candidatesfor?(int?u?=?r?>>>?1;?u?+?m?-?(r?=?u?%?bound)?<?0;?u?=?mix(nextSeed())?>>>?1);}return?r;}复制代码nextSeed()
nextSeed()方法的作用是获取随机种子,代码很简单,就是通过UNSAFE类获取当前线程的threadLocalRandomSeed属性,并且将原来的threadLocalRandomSeed加上GAMMA设置成新的threadLocalRandomSeed。
final?long?nextSeed()?{ Thread?t;?long?r;?//?read?and?update?per-thread?seedUNSAFE.putLong(t?=?Thread.currentThread(),?SEED,?r?=?UNSAFE.getLong(t,?SEED)?+?GAMMA);return?r;}复制代码小结:
ThreadLocalRandom为什么线程安全?是因为它将随机种子保存在当前Thread对象的threadLocalRandomSeed变量中,这样每个线程都有自己的中国燕窝溯源码管理随机种子,实现了线程级别的隔离,所以ThreadLocalRandom也并不需要像Random通过自旋锁和cas来保证随机种子的线程安全性。在高并发的场景下,效率也会相对较高。
注:各位有没有发现ThreadLocalRandom保证线程安全的方式和ThreadLocal有点像呢
需要注意的点:
1.ThreadLocalRandom是单例的。
2.我们每个线程在获取随机数之前都需要调用一下ThreadLocalRandom.current()来初始化当前线程的随机种子。
3.理解ThreadLocalRandom需要对UnSafe类有所了解,它是Java提供的一个可以直接通过内存对变量进行获取和修改的一个工具类。java的CAS也是通过这个工具类来实现的。
原文:“/post/”
七天杀上GitHub榜首!Java并发编程深度解析实战,JUC底层原理揭秘
在多核CPU和多线程技术普及的当今,我们面对的不再是多年前对于线程开启时机的问题。如今,无论是开发人员还是技术开发者,都需要深入了解多线程技术的方方面面。本文将从操作系统原理的角度,全面解析多线程技术,涵盖基础知识到高级进阶,分享作者多年的工作经验和踩坑后的教训。
多线程编程技术已经成为现代软件开发不可或缺的部分。然而,对于很多开发者来说,尽管有各种库和运行环境对操作系统多线程接口的封装,他们仍然面对着复杂的多线程逻辑,甚至只是简单调用库的“业务”程序员。本文旨在从基础出发,深入浅出地讲解多线程技术的各个层面。
本文分为章,从Java线程的实践及原理揭秘开始,逐步深入到synchronized实现原理、volatile解决可见性和有序性问题、J.U.C中的重入锁和读写锁、线程通信中的条件等待机制、J.U.C并发工具集实战、并发编程必备工具、阻塞队列设计原理及实现、并发安全集合原理及源码、线程池设计原理、以及Java并发编程中的异步编程特性。每一章节都基于作者的云豹直播源码破解网页经验总结和踩坑后的教训,为读者提供全面而深入的指导。
如果您对这份手册感兴趣并希望深入学习,欢迎您点赞并关注。获取完整内容的方式非常简单,只需点击下方链接即可。让我们一起探索多线程技术的奥秘,提升编程技能,迈向技术的高峰。
juc是治疗什么的
JUC是Java并发编程的核心工具集。以下是关于JUC的详细解释: 一、JUC概述 Java并发编程是Java编程语言的一个重要部分,而Java并发工具集则为开发者提供了一系列用于并发编程的核心工具。这些工具能够帮助开发者创建多线程应用程序,实现高效的并发处理,提高系统的整体性能和响应能力。 二、JUC的主要组件 JUC包含多个组件,主要包括: 1. 线程池相关类:如ExecutorService、ThreadPoolExecutor等,它们提供了线程池的管理和使用,有效减少创建和销毁线程的开销。 2. 同步辅助类:如CountDownLatch、CyclicBarrier等,这些类提供了同步机制,帮助控制多线程间的协作。 3. 原子变量类:如AtomicInteger、AtomicReference等,这些类提供了原子操作,保证多线程环境下的数据一致性。 4. 阻塞队列及相关操作:如BlockingQueue接口及其实现类,为线程间的数据交换提供了高效的阻塞队列。 三、JUC的应用领域 JUC广泛应用于需要处理并发问题的领域,如: * 高并发Web应用:处理大量用户请求,提高系统吞吐能力。 * 大数据处理:在数据处理和分析过程中,利用并发编程提高处理速度。 * 分布式系统:在分布式环境中,利用并发编程实现各节点间的协同工作。 四、总结 JUC为Java开发者提供了丰富的口红机源码app开发并发编程工具,帮助开发者创建高效、稳定的多线程应用程序。无论是Web应用、大数据处理还是分布式系统,JUC都发挥着重要的作用。熟练掌握JUC的使用,对于提高Java开发者的技能水平具有重要的价值。Java并发指南:JUC中常用的Unsafe和Locksupport
本文转自网络,内容主要介绍Java并发技术中的 Unsafe 类与 Locksupport,它们在Java基础类库以及高性能开发库中扮演着重要角色。虽然Java最初设计时强调安全,但 sun.misc.Unsafe 类提供了直接操作内存和线程的底层能力,被广泛用于JDK内部如java.nio 和 java.util.concurrent等包中。然而,它不建议在生产环境中使用,因为API安全性、灵活性和稳定性较差。
Unsafe 类提供了多种功能,包括内存管理、非常规对象实例化、操作类和对象、数组操作以及多线程同步。它通过一系列方法,如allocateMemory, getInt, monitorEnter等,实现了对Java内存、线程的直接访问与控制。值得注意的是,Unsafe 类中的monitorEnter等方法已被标记为过时,不推荐使用。
Locksupport 是JDK中用来创建锁和其他同步工具类的基础线程阻塞原语,能够实现类似于join()、wait()/notifyAll()的功能,使线程能够自由地阻塞与释放。在Java锁与同步器框架的核心类AQS中,正是通过调用LockSupport.park()和LockSupport.unpark()实现线程的阻塞与唤醒。
Locksupport 提供了park()和unpark()方法来实现线程的阻塞与解除阻塞。这些方法通过与Unsafe类的交互,实现了线程控制的底层实现。值得注意的是,Locksupport的使用必须成对出现,即park()与unpark()配合使用,情侣做记录网站源码否则可能导致线程阻塞。
在使用Locksupport时,应避免与其他同步机制如wait()、notify()等冲突,因为它们属于不同的实现机制。另外,Locksupport的park()方法可以通过Object blocker参数提供更具体的阻塞信息,帮助开发者定位问题。
总之,Unsafe 类与 Locksupport 提供了对Java内存与线程底层操作的强大能力,它们在提升性能和实现特定功能时具有重要作用,但使用时需谨慎,确保不会引入安全风险或导致系统不稳定。
JUC之CountDownLatch与CyclicBarrier
在java.util.concurrent包中,CountDownLatch与CyclicBarrier是用于线程同步的工具类。CountDownLatch允许一个或多个线程等待直到其他线程完成一组操作。类比于等待所有小伙伴上车后再出发的春游场景,通过使用CountDownLatch,我们可以更简洁地实现这一需求。CountDownLatch包含创建、减计数以及等待计数器为零等方法。
CyclicBarrier则允许一组线程在达到共同屏障点后等待彼此,直至所有线程都执行完毕。这个工具类可以模拟赛跑比赛,所有运动员在起跑线前等待,直到所有运动员准备完毕,比赛正式开始。CyclicBarrier具有构造方法、等待所有线程达到屏障的方法以及重置计数值的能力。
CountDownLatch与CyclicBarrier在使用场景和功能上有所区别。CountDownLatch适用于一个或多个线程等待其他线程执行完毕后继续执行的场景,而CyclicBarrier则适用于一组线程在达到共同点后等待彼此的场景。CountDownLatch强调计数,最终计数为零后线程执行,CyclicBarrier强调共同等待,直到所有线程到达同一屏障点。
总结来说,CountDownLatch与CyclicBarrier均提供了线程同步的功能,但CountDownLatch侧重于计数控制,而CyclicBarrier侧重于共同等待机制。在实际开发中,根据具体场景选择合适的工具类,可以更高效地管理线程同步问题。
八股文JUC知识总结
并发编程,如同一股清流,旨在最大化多核CPU的潜力,提升性能和处理能力,但同时也伴随着一些挑战,如内存泄露、频繁的上下文切换、线程安全难题和死锁的威胁。其中,上下文切换是CPU通过时间片机制切换线程的关键步骤。
探索并行编程的路径多种多样,你可选择四种方式来创建线程:一是继承自Java的Thread类,二是通过实现Runnable接口,三是依赖Callable接口的返回值特性,四是借助Executor工具类的灵活性。Runnable模式虽无返回值,但Callable则提供了返回值处理和异常管理的可能。
线程的生命周期包括五个阶段:新建、就绪、运行、阻塞(包括等待、同步阻塞和其他形式)以及消亡。并发编程的三大基石——原子性、可见性和有序性,是保证并发操作正确性的核心要素,Atomic类、synchronized和Lock接口为我们提供了相应的解决方案。synchronized,作为Java的灵魂关键字,通过互斥、缓存刷新和处理器顺序保证了这三大特性。
深入理解synchronized的底层机制,你会发现它拥有从偏向锁、轻量级锁到重量级锁的升级过程。非竞争时,偏向锁能快速获取,而在竞争中会升级为轻量级锁,自旋尝试不阻塞。然而,当竞争加剧,锁会变为重量级,造成性能损失。volatile关键字则强调可见性和有序性,而非原子性操作。内存屏障则守护数据一致性,防止重排序带来的问题。
对于更高级的锁控制,如ReentrantLock,它提供了可重入、中断选项和公平性选择,比synchronized更为灵活。AQS(AbstractQueuedSynchronizer)则作为基础框架,为构建复杂同步器提供了强大支持。在并发工具中,线程池是必不可少的一部分,它能节省资源、优化响应速度,如FixedThreadPool、ScheduledThreadPool等各有其适用场景。
信号量Semaphore,倒计时器CountDownLatch,以及同步执行器CyclicBarrier,它们各有其特定的并发控制用途。原子类,如AtomicInteger,确保操作的不可分割性。ThreadLocal则为每个线程维护独立的局部变量副本,适用于线程独立数据的处理,但需注意内存泄漏问题。
最后,Thread类的方法如start()启动任务,run()执行操作,yield()让出CPU,join()等待线程结束,而stop()已被弃用。sleep()和isAlive()则是控制线程暂停和判断存活状态的工具。至于同步控制,wait(), notify(), notifyAll()在并发编程中发挥着至关重要的作用。
硬核干货: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的源码,深入探讨线程池的扩展和调度机制。敬请关注,期待下文的详细解析。并发编程的基石——AQS类
AQS 简介
AbstractQueuedSynchronizer (AQS) 是JUC包的核心类,用于构建同步工具如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore和LimitLatch。它抽象了同步器构建过程中的通用关注点,例如资源管理、线程阻塞与唤醒、等待队列等。用户仅需解决同步器资源定义和状态更新两个关键问题。
同步器资源的定义:ReentrantLock表示独占锁,CountDownLatch表示倒计时器,Semaphore表示信号量或令牌,其状态定义了锁可用性、线程等待状态等。ReentrantReadWriteLock则表示共享读锁和独占写锁。
AQS 原理
AQS 管理同步状态并提供API供用户重写以实现自定义同步器。它通过 getState、setState 和 compareAndSetState 操作管理状态,并通过 CLH 队列管理等待线程。
状态管理:使用单个 int 来保存同步状态,提供 API 来读取和更新。
线程阻塞与唤醒:引入 LockSupport 类来实现线程的阻塞和唤醒。
等待队列:基于 CLH 双向循环链表实现,确保同步状态下的线程有序等待和唤醒。
节点定义与队列定义:节点封装线程状态,队列实现线程结构管理,支持取消、唤醒等操作。
AQS 的方法介绍
用户需要重写 tryAcquire、tryRelease 等方法来实现自定义同步器。这些方法提供获取和释放资源的逻辑,无需用户关心线程等待队列的细节。
钩子方法描述:tryAcquire 和 tryRelease 分别用于独占和共享模式下的资源获取和释放。
AQS 提供的一系列模板方法
acquire 和 release 方法是获取和释放资源的顶层入口,acquire 方法自旋尝试获取资源,release 方法则根据资源释放情况唤醒等待线程。
unparkSuccessor 方法用于唤醒等待队列中的下一个线程,确保资源被释放后能尽快分配。
acquireShared 和 releaseShared 方法适用于共享模式,实现资源的获取和释放,并根据情况唤醒等待线程。
总结:AQS 通过抽象和封装,简化了同步器的实现,提供了一套灵活、高效的线程同步机制。用户仅需关注同步资源的定义和状态更新逻辑,其他同步细节如线程等待队列的管理由 AQS 自动处理。
2024-12-23 23:30
2024-12-23 21:54
2024-12-23 21:54
2024-12-23 21:40
2024-12-23 21:33