JUC
文章平均质量分 67
学习JUC
shall_zhao
这个作者很懒,什么都没留下…
展开
-
CopyOnWriteArrayList
CopyOnWriteArraySet 是它的马甲 底层实现采用了 写入时拷贝 的思想,增删改操作会将底层数组拷贝一份,更改操作在新数组上执行,这时不影响其它线程的。这里的源码版本是 Java 11,在 Java 1.8 中使用的是可重入锁而不是 synchronized适合『读多写少』的应用场景。原创 2024-05-02 21:55:11 · 222 阅读 · 0 评论 -
ConcurrentLinkedQueue 原理
例如之前讲的 Tomcat 的 Connector 结构时,Acceptor 作为生产者向 Poller 消费者传递事件信息时,正是采用了ConcurrentLinkedQueue 将 SocketChannel 给 Poller 使用。ConcurrentLinkedQueue 的设计与 LinkedBlockingQueue 非常像,也是。原创 2024-05-02 21:48:52 · 324 阅读 · 0 评论 -
LinkedBlockingQueue 原理
初始化链表 last = head = new Node(null);Dummy 节点用来占位,item 为 null。主要列举 LinkedBlockingQueue 与 ArrayBlockingQueue 的性能比较。再来一个节点入队 last = last.next = node;当一个节点入队 last = last.next = node;由 put 唤醒 put 是为了避免信号不足。在于用了两把锁和 dummy 节点。原创 2024-05-02 21:35:53 · 810 阅读 · 0 评论 -
ConcurrentHashMap
这是为了观察 e 节点和 next 节点的状态,Thread-0 单步执行到 594 行,再 594 处再添加一个断点(条件Thread.currentThread().getName().equals(“Thread-0”))这时可以在 Variables 面板观察到 e 和 next 变量,使用 view as -> Object 查看节点状态。get 时并未加锁,用了 UNSAFE 方法保证了可见性,扩容过程中,get 先发生就从旧表取内容,get 后发生就从新表取内容。原创 2024-05-02 21:19:56 · 992 阅读 · 0 评论 -
线程安全集合类概述
重点介绍 java.util.concurrent.* 下的线程安全集合类,可以发现它们有规律,里面包含三类关键词:Blocking、CopyOnWrite、Concurrent。出现时间比较早,方法都是用synchronized修饰,并发效率比较低,有更好地替代。设计思想是装饰器模式,但本质没有发生本质变化,还是使用synchronized修饰。遗留的线程安全集合如 Hashtable , Vector。Blocking 大部分实现基于锁,并提供用来阻塞的方法。Concurrent 类型的容器。原创 2024-05-02 15:28:17 · 1 阅读 · 0 评论 -
CyclicBarrier
构造时设置『计数个数』,每个线程执行到某个需要“同步”的时刻调用 await() 方法进行等待,当等待的线程数满足『计数个数』时,继续执行。注意 CyclicBarrier 与 CountDownLatch 的主要区别在于 CyclicBarrier 是可以重用的 CyclicBarrier 可以被比。使用我们的CyclicBarrier ,要是我们的线程池中的线程数和CyclicBarrier 的数量大小一样才能发挥CyclicBarrier 的人满发车效果。原创 2024-05-02 15:15:59 · 2 阅读 · 0 评论 -
CountdownLatch
其中构造参数用来初始化等待计数值,await() 用来等待计数归零,countDown() 用来让计数减一。用来进行线程同步协作,等待所有线程完成倒计时。可以配合线程池使用,改进如下。原创 2024-05-02 14:59:45 · 0 阅读 · 0 评论 -
Semaphore
接下来 Thread-0 竞争成功,permits 再次设置为 0,设置自己为 head 节点,断开原来的 head 节点,unpark 接下来的 Thread-3 节点,但由于 permits 是 0,因此 Thread-3 在尝试不成功后再次进入 park 状态。假设其中 Thread-1,Thread-2,Thread-4 cas 竞争成功,而 Thread-0 和 Thread-3 竞争失败,进入 AQS 队列park 阻塞。刚开始,permits(state)为 3,这时 5 个线程来获取资源。原创 2024-05-02 14:29:21 · 362 阅读 · 0 评论 -
JUC——读写锁原理
t3 进入 sync.releaseShared(1) 中,调用 tryReleaseShared(1) 让计数减一,这回计数为零了,进入doReleaseShared() 将头节点从 -1 改为 0 并唤醒老二,即。😉 这次自己是老二,并且没有其他竞争,tryAcquire(1) 成功,修改头结点,流程结束。这时会走到写锁的 sync.release(1) 流程,调用 sync.tryRelease(1) 成功,变成下面的样子。下一个节点不是 shared 了,因此不会继续唤醒 t4 所在节点。原创 2024-05-02 13:59:11 · 596 阅读 · 0 评论 -
公开课—京东生产环境海量数据架构优化实战
早期都是单库单表。原创 2024-04-30 21:48:36 · 994 阅读 · 0 评论 -
JUC——ReentrantLock 原理
执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的waitStatus 改为 -1。默认是不可打断的,在此模式下,即使它被打断,仍会驻留在 AQS 队列中,一直要等到获得锁后方能得知自己被打断了。unpark AQS 队列中的下一个节点,竞争锁,假设没有其他竞争线程,那么 Thread-1 竞争成功。总结起来就是,加锁时重入,让state自增,解锁时让state–,减为零真正解锁。原创 2024-04-30 19:47:46 · 407 阅读 · 0 评论 -
JUC——AQS原理
全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁getState - 获取 state 状态setState - 设置 state 状态compareAndSetState - cas 机制设置 state 状态独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源。原创 2024-04-30 17:03:26 · 551 阅读 · 0 评论 -
Fork/Join
提交给 Fork/Join 线程池的任务需要继承 RecursiveTask(有返回值)或 RecursiveAction(没有返回值),例如下面定义了一个对 1~n 之间的整数求和的任务。Fork/Join 是 JDK 1.7 加入的新的线程池实现,它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型运算。Fork/Join 在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运算效率。所以拆分的好不好会很大的影响效率。原创 2024-04-30 16:25:16 · 155 阅读 · 0 评论 -
ThreadPoolExecutor
在『任务调度线程池』功能加入(jdk1.5)之前,可以使用 java.util.Timer 来实现定时功能,Timer 的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。当线程数达到 corePoolSize 并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue 队列排队,直到有空闲的线程。线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。原创 2024-04-30 15:33:04 · 706 阅读 · 0 评论 -
线程池应用——定时任务
如何让每周四 18:00:00 定时执行任务?原创 2024-04-30 15:10:34 · 111 阅读 · 0 评论 -
异步模式——工作线程模式
让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现就是线程池,也体现了经典设计模式中的享元模式。例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果为每位客人都配一名专属的服务员,那么成本就太高了(对比另一种多线程设计模式:Thread-Per-Message)注意,不同任务类型应该使用不同的线程池,这样能够避免饥饿,并能提升效率。原创 2024-04-30 14:36:45 · 251 阅读 · 0 评论 -
final原理
在这个测试中,如果TestFinal中的A加了final,那么类UseFinal1中使用A的时候就发现,TestFinal直接把A复制了一份放在了UseFinal1的栈中,如果不加final,那么UseFinal1使用A的时候要调用GETSTATIC方法去获取。同理TestFinal中的B,虽然是个很大的数,但是加了final,UseFinal1在使用的时候,发现是从常量池中取得(就是复制到常量池中了),要是不加final,UseFinal1使用的时候,还是要GETSTATIC。从字节码中才能看出来,原创 2024-04-29 19:10:05 · 237 阅读 · 0 评论 -
共享模型之不可变——不可变设计、享元模式
定义英文名称:Flyweight pattern. 当需要重用数量有限的同一类对象时出自归类。原创 2024-04-29 17:36:18 · 904 阅读 · 0 评论 -
共享模型之不可变——日期转换的问题
如果一个对象在不能够修改其内部状态(属性),那么它就是线程安全的,因为不存在并发修改啊!下面的代码在运行时,由于 SimpleDateFormat 不是线程安全的。不可变对象,实际是另一种避免竞争的方式。原创 2024-04-29 16:45:40 · 295 阅读 · 0 评论 -
共享模型之无锁——Unsafe
Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得static {try {原创 2024-04-29 16:35:21 · 116 阅读 · 0 评论 -
共享模型之无锁——原子累加器
性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]…因为 Cell 是数组形式,在内存中是连续存储的,一个 Cell 为 24 字节(16 字节的对象头和 8 字节的 value),因此缓存行可以存下 2 个的 Cell 对象。的原理是在使用此注解的对象或字段的前后各增加 128 字节大小的padding,从而让 CPU 将对象预读至缓存时占用不同的缓存行,这样,不会造成对方缓存行的失效。缓存与内存的速度比较。原创 2024-04-29 16:06:33 · 272 阅读 · 0 评论 -
共享模型之无锁——字段更新器
利用字段更新器,可以针对对象的某个域(Field)进行原子操作,只能配合 volatile 修饰的字段使用,否则会出现异常。字段更新器保护的是某个对象里的属性/成员变量,保证多个线程访问同一个对象的成员变量的时候,成员变量的线程安全性。原创 2024-04-29 14:29:39 · 129 阅读 · 0 评论 -
共享模型之无锁-原子数组
【代码】JUC-原子数组。原创 2024-04-29 14:20:59 · 184 阅读 · 0 评论 -
共享模型之无锁-原子整数
AtomicStampedReference 可以给原子引用加上版本号,追踪原子引用整个的变化过程,如: A -> B -> A ->只要有其它线程【动过了】共享变量,那么自己的 cas 就算失败,这时,仅比较值是不够的,需要再加一个版本号。C ,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了几次。但是有时候,并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了。可以注释掉打扫卫生线程代码,再观察输出。为什么需要原子引用类型?原创 2024-04-29 14:06:48 · 249 阅读 · 0 评论 -
happens-before规则
happens-before 规定了对共享变量的写操作对其它线程的读操作可见,它是可见性与有序性的一套规则总结,抛开以下 happens-before 规则,JMM 并不能保证一个线程对共享变量的写,对于其它线程对该共享变量的读可见。变量都是指成员变量或静态成员变量。原创 2024-04-28 20:26:54 · 136 阅读 · 0 评论 -
volatile原理
volatile 的底层实现原理是内存屏障,Memory Barrier(Memory Fence)原创 2024-04-28 19:59:54 · 643 阅读 · 0 评论 -
JVM的有序性
主频的概念大家接触的比较多,而 CPU 的 Clock Cycle Time(时钟周期时间),等于主频的倒数,意思是 CPU 能够识别的最小时间单位,比如说 4G 主频的 CPU 的 Clock Cycle Time 就是 0.25 ns,作为对比,我们墙上挂钟的。情况2:线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为1。条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了。原创 2024-04-28 19:22:20 · 761 阅读 · 0 评论 -
同步模式之Balking
Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需再做了,直接结束返回。原创 2024-04-28 18:57:00 · 302 阅读 · 0 评论 -
同步模式之顺序控制
park 和 unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』,不需要『同步对象』和『运行标记』线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现?也可以使用ReentrantLock的await 和singal来解决。比如,必须先 2 后 1 打印。原创 2024-04-28 15:56:53 · 403 阅读 · 0 评论 -
ReentrantLock
synchronized 中也有条件变量,就是我们讲原理时那个 waitSet 休息室,当条件不满足时进入 waitSet 等待。ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比。可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。我在等待锁的过程,其他锁可以通过interrupt打断我。立刻失败,主动的方式避免死锁。原创 2024-04-28 14:50:17 · 298 阅读 · 0 评论 -
线程活跃性——死锁,活锁,饥饿
很多教程中把饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,讲读写锁时会涉及饥饿问题。这种线程没有按预期结束,执行不下去的情况,归类为【活跃性】问题,除了死锁以外,还有活锁和饥饿者两种情况。下面我讲一下我遇到的一个线程饥饿的例子,先来看看使用顺序加锁的方式解决之前的死锁问题。活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如。有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁。解决活锁办法就是把两个线程的休息时间设置成随机的。原创 2024-04-28 13:25:53 · 220 阅读 · 0 评论 -
多把不相干的锁
现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低。一间大屋子有两个功能:睡觉、学习,互不相干。解决方法是准备多个房间(多个对象锁)改进,多准备几个房间。原创 2024-04-28 12:39:14 · 172 阅读 · 0 评论 -
重新理解线程状态转换
从Java层面分了六种状态,在细娃一下状态转换的过程。单向箭头表示单向转换,双向箭头表示可以互相转换。在idea的调试界面,Blocked显示的是Monitor,Runable显示的是Running。当调用 t.start() 方法时,由 NEW —> RUNNABLE。用 synchronized(obj) 获取了对象锁后。用 synchronized(obj) 获取了对象锁后。当前线程所有代码运行完毕,进入 TERMINATED。假设有线程 Thread t。原创 2024-04-28 11:43:06 · 949 阅读 · 0 评论 -
Park&Unpark
unpark既可以在park之前调用,也可以在park之后调用。与 Object 的 wait & notify 相比。它们是 LockSupport 类中的方法。这里park的状态其实就是wait的状态。先 park 再 unpark。先 unpark 再 park。原创 2024-04-28 11:20:10 · 317 阅读 · 0 评论 -
设计模式——异步模式之生产者消费者
要点与前面的保护性暂停中的 GuardObject 不同,不需要产生结果和消费结果的线程一一对应消费队列可以用来平衡生产和消费的线程资源生产者仅负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据JDK 中各种阻塞队列,采用的就是这种模式。原创 2024-04-28 11:06:27 · 130 阅读 · 0 评论 -
设计模式——保护性暂停
即 Guarded Suspension,用在一个线程等待另一个线程的执行结果要点有一个结果需要从一个线程传递到另一个线程,让他们关联同一个 GuardedObject如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)JDK 中,join 的实现、Future 的实现,采用的就是此模式因为要等待另一方的结果,因此归类到同步模式。原创 2024-04-28 08:42:41 · 431 阅读 · 0 评论 -
wait notify 的正确姿势
【代码】wait notify 的正确姿势。原创 2024-04-28 07:20:15 · 143 阅读 · 0 评论 -
wait notify
obj.wait() 让进入 object 监视器的线程到 waitSet 等待obj.notify() 在 object 上正在waitSet 等待的线程中挑一个唤醒obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法log.debug("执行....");// 让线程在obj上一直等待下去。原创 2024-04-28 07:20:00 · 242 阅读 · 0 评论 -
Monitor 概念
后来他自己放假回老家了,这时小女回来了(她也要用这些房间),结果就是得一个个地擦掉小南刻的名字,升级为挂书包的方式。小南和小女商量了一下,约定不锁门了,而是谁用房间,谁把自己的书包挂在门口,但他们的书包样式都一样,因此每次进门前得翻翻书包,看课本是谁的,如果是自己的,那么就可以进门,这样省的上锁解锁了。于是,小南干脆在门上刻上了自己的名字:【小南专属房间,其它人勿用】,下次来用房间时,只要名字还在,那么说明没人打扰,还是可以安全地使用房间。小女是要用房间,但使用的时间上是错开的,小南白天用,小女晚上用。原创 2024-04-27 21:52:22 · 524 阅读 · 0 评论 -
线程八锁——synchronized加在方法上
其实就是考察 synchronized 锁住的是哪个对象。原创 2024-04-26 19:32:24 · 137 阅读 · 0 评论