
并发编程
文章平均质量分 67
ProMan_XY
这个作者很懒,什么都没留下…
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
并发编程设计模式——生产者-消费者模式(四十五)
概述:生产者 - 消费者模式的核心是一个任务队列,生产者线程生产任务,并将任务添加到任务队列中,而消费者线程从任务队列中获取任务并执行。Java 线程不是越多越好,适量即可。而生产者 - 消费者模式恰好能支持你用适量的线程。支持异步,并且能够平衡生产者和消费者的速度差异(任务队列出力了)解耦,减少代码或组件之间的依赖关系。生产者 - 消费者模式的优点。支持分阶段提交以提升性能。支持批量执行以提升性能。原创 2025-06-23 07:45:00 · 760 阅读 · 0 评论 -
并发编程设计模式——两阶段终止模式模式(四十四)
标答:当线程的run方法中执行业务逻辑时候调用到第三方类库提供的方法,保不准第三方类库在捕获到异常时,不会重置interrupt标志位。导致线程的中断状态被清空,造成影响。建议自己实现一个线程终止标志位,这样即使调用第三方方法也不会影响到自定义的线程中止标志位。两阶段终止模式需要注意两个关注点,一个是仅检查终止标志位是不够的,因为线程的状态可能处于休眠状态;另一个就是仅检查线程的中断状态也是不够的,因为我们依赖的第三方类库很可能没有正确的处理中断异常。案例:用两阶段终止模式终止监控操作。原创 2025-06-23 07:00:00 · 290 阅读 · 0 评论 -
并发编程设计模式——Thread-Per-Message模式(四十二)
Thread-Per-Message模式:为每个任务分配一个独立的线程。经典应用:网络编程里服务端的实现(服务端为每个客户端请求创建一个独立的线程,当线程处理完请求后,自动销毁)。用 Thread 实现 Thread-Per-Message 模式(这个实现用于对比,很坑)如何理解 Thread-Per-Message 模式(委托代办)Fiber 实现 Thread-Per-Message 模式。最简单实用的分工方法。原创 2025-02-12 07:45:00 · 279 阅读 · 0 评论 -
并发编程设计模式——Copy-On-Write模式(四十一)
Java 提供了 CopyOnWriteArrayList,为什么没有提供 CopyOnWriteLinkedList 呢?Copy-on-Write 模式的应用领域。原创 2024-11-14 08:30:00 · 506 阅读 · 0 评论 -
并发编程设计模式——Immutability模式(四十)
问题:如果具备不可变性的类,需要提供类似修改的功能,创建一个新的不可变对象就行了,这是与可变对象的一个重要区别,可变对象往往是修改自己的属性。所有的修改操作都创建一个新的不可变对象:这样会不会造成创建的对象太多了,有点太浪费内存呢?Foo 具备不可变性,线程安全,但是类 Bar 并不是线程安全的,类 Bar 中持有对 Foo 的引用 foo,对 foo 这个引用的修改在多线程中并不能保证可见性和原子性。本意是 A 用锁 al,B 用锁 bl,各自管理各自的,互不影响。快速实现具备不可变性的类。原创 2024-11-14 08:15:00 · 837 阅读 · 0 评论 -
并发编程设计模式——Balking模式(三十九)
多线程下,维护一个共享状态满足某个条件时,执行业务逻辑;当不满足时则立即放弃。通常用互斥锁来确保共享状态线程安全,如果不需要保证共享状态原子性,也可以用 volitle 修饰,替换互斥锁。Balking 模式本质上是一种规范化地解决“多线程版本的 if”的方案,其实就是对if判断的加锁设计。并抽象一个方法出来。使用 volatile 的前提是对原子性没有要求。用 volatile 实现 Balking 模式。Balking 模式的经典实现。这样的好处是将并发处理逻辑和业务逻辑分开。Balking 模式。原创 2024-11-11 08:30:00 · 466 阅读 · 0 评论 -
并发编程设计模式——Guarded Suspension模式(三十八)
概述:多线程下,A线程有个受保护的操作,在执行过程中需要等待满足某个条件,这个条件由B线程控制,条件不满足,A线程则挂起,直到条件满足才继续执行。例子:核心是:get() 方法通过条件变量的 await() 方法实现等待,onChanged() 方法通过条件变量的 signalAll() 方法实现唤醒功能。单线程场景中,if 语句是不需要等待的,因为在只有一个线程的条件下,如果这个线程被阻塞,那就没有其他活动线程了,这意味着 if 判断条件的结果也不会发生变化了。”来理解这个模式会更简单。原创 2024-11-11 08:15:00 · 249 阅读 · 0 评论 -
并发编程工具集——Fork/Join-下(三十七)
这个共享的 ForkJoinPool 默认的线程数是 CPU 的核数。用 Fork/Join 计算斐波那契数列。模拟 MapReduce 统计单词数量。ForkJoinPool 工作原理。原创 2024-09-26 10:18:02 · 771 阅读 · 0 评论 -
并发编程工具集——Fork/Join-上(三十六)
Fork/Join 的使用。原创 2024-09-24 08:00:00 · 462 阅读 · 0 评论 -
并发编程工具集——CompletionService(三十五)
例如你需要提供一个地址转坐标的服务,为了保证该服务的高可用和性能,你可以并行地调用 3 个地图服务商的 API,然后只要有 1 个正确返回了结果 r,那么地址转坐标这个服务就可以直接返回 r 了。通过调用 cs.take().get(),我们能够拿到最快返回的任务执行结果,只要我们拿到一个正确返回的结果,就可以取消所有任务并且返回最终结果了。Dubbo 中有一种叫做 Forking 的集群模式,这种集群模式下,支持并行地调用多个查询服务,只要有一个成功返回结果,整个服务就可以返回了。原创 2024-09-24 07:15:00 · 1658 阅读 · 0 评论 -
并发编程工具集——CompletionStage (三十四)
CompletionStage 接口里面描述 AND 汇聚关系,主要是 thenCombine、thenAcceptBoth 和 runAfterBoth 系列的接口,这些接口的区别也是源自 fn、consumer、action 这三个核心参数不同。applyToEither、acceptEither 和 runAfterEither。描述 AND 汇聚关系。都是靠回调函数来解决的。描述 OR 汇聚关系。原创 2024-09-02 08:30:00 · 1014 阅读 · 0 评论 -
并发编程工具集——CompletableFuture(三十三)
前两个方法:runAsync(Runnable runnable)和supplyAsync(Supplier<U> supplier),它们之间的区别是:Runnable 接口的 run() 方法没有返回值,而 Supplier 接口的 get() 方法是有返回值的。CompletableFuture 的核心优势。如何理解 CompletionStage 接口。原创 2024-09-02 08:00:00 · 498 阅读 · 0 评论 -
并发编程工具集——Callable 任务如何运行以及获取执行结果-下篇(三十二)
提接上文~~重点来了!看懂了几乎就明白线程池执行任务时的beforeExecute、afterExecute方法的所起的作用了(比如经常在afterExecute方法里面做一些线程池任务运行时间的统计工作)。总结以下点:Callable任务被submit时,会生成一个FutureTask对象,封装Callable,在FutureTask的run方法里面执行Callable#call方法,并且调用返回一个Future,它实际上是一个FutureTask对象,通过获取Callable任务的执行结果。原创 2024-08-20 08:45:00 · 838 阅读 · 0 评论 -
并发编程工具集——Callable 任务如何运行以及获取执行结果-上篇(三十一)
向线程池提交Callable任务,会创建一个新线程(执行任务的线程)去执行这个Callable任务,但是通过Future#get获取任务的执行结果是在提交任务的调用者线程中,那问题一:调用者线程如何获取执行任务的线程的结果?在JDK中,有2种类型的任务,Runnable和Callable,但是具体到线程池执行任务的方法,它只接收Runnable任务,那问题二:Callable任务是提交给线程池后是如何执行的呢?原创 2024-08-20 08:30:00 · 766 阅读 · 0 评论 -
并发编程工具集——非阻塞算法lock-free和wait-free-下篇(三十)
CAS实现的过程是先取出内存中某时刻的数据,在下一时刻比较并替换。尽管1号线程的CAS操作成功,但是不代表这个过程就是没有问题的。我们来看个具体例子:现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B:head→A→B后希望用CAS将栈顶替换为B在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下,而对象B此时处于游离状态:head→A→C→DB。原创 2024-08-20 08:00:00 · 297 阅读 · 0 评论 -
并发编程工具集——非阻塞算法lock-free和wait-free-上篇(二十九)
使用wait-free的数据结构的应用程序中,与其将原来使用互斥的算法改造为wait-free的算法,不如直接使用基于wait-free算法开发的stack、queue、set和map。这个算法是lock-free的但是不是wait-free的。如果在进行这三步的时候,第一步读出的金额和第三步更新要用新值去替换的金额不一致的情况下,命令直接失败,然后操作回滚到第一步重新执行。所以对于算法来说达到lock-free不一定能达到wait-free,但是达到wait-free的算法一定是lock-free的。原创 2024-08-08 09:00:00 · 531 阅读 · 0 评论 -
并发编程工具集——动态更新的工作原理(二十八)
判断工作线程是否是大于最大线程数,如果大于,则对空闲线程发起中断请求。经过前面两个方法的分析,我们知道了最大线程数和核心线程数可以动态调整。效果实现了,我先看一下原理是什么。这个地方就很简单了,逻辑不太复杂。然后用传递进来的值,覆盖原来的值。首先是参数合法性校验。原创 2024-08-08 07:30:00 · 359 阅读 · 0 评论 -
并发编程工具集——Future(二十七)
如何使用:FutureTask 实现了 Runnable 和 Future 接口,由于实现了 Runnable 接口,所以可以将 FutureTask 对象作为任务提交给 ThreadPoolExecutor 去执行,也可以直接被 Thread 执行;又因为实现了 Future 接口,所以也能用来获得任务的执行结果。不过需要注意的是:这两个 get() 方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用 get() 方法的线程会阻塞,直到任务执行完才会被唤醒。如何获取任务执行结果。原创 2024-08-01 08:30:00 · 1141 阅读 · 0 评论 -
并发编程工具集——线程池(二十六)
Java 在 1.6 版本还增加了 allowCoreThreadTimeOut(boolean value) 方法,如何使用 Java 中的线程池(线程池的七个参数)线程池与其他池化资源的区别。原创 2024-08-01 08:00:00 · 984 阅读 · 0 评论 -
并发编程工具集——并发容器-下(二十五)
ConcurrentSkipListMap 里面的 SkipList 本身就是一种数据结构,被称为跳表,跳表插入、删除、查询操作平均的时间复杂度是 O(log n),理论上和并发线程数没有关系,所以在并发度非常高的情况下,如果对 ConcurrentHashMap 的性能不满意,可以尝试一下 ConcurrentSkipListMap。CopyOnWriteArrayList迭代器是只读的,不支持增删改,因为遍历的是一个快照,对快照增删改显然没有用。Fail-Fast(快速失败机制)原创 2024-07-30 07:45:00 · 897 阅读 · 0 评论 -
并发编程工具集——并发容器-上(二十四)
JDK1.5之后提供,性能稳定且高。同步容器及其注意事项。并发容器及其注意事项。原创 2024-07-26 08:45:00 · 294 阅读 · 0 评论 -
并发编程工具集——原子类(Compare And Swap)下(二十三)
DoubleAccumulator、DoubleAdder、LongAccumulator 和 LongAdder,这四个类仅仅用来执行累加操作,相比原子化的基本数据类型,速度更快,但是不支持 compareAndSet() 方法。相关实现有 AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray,利用这些原子类,我们可以原子化地更新数组里面的每一个元素。这些类提供的方法和原子化的基本数据类型的区别仅仅是:每个方法多了一个数组的索引参数。原创 2024-07-26 08:00:00 · 777 阅读 · 0 评论 -
并发编程工具集——原子类(Compare And Swap)下(二十三)
原子化的基本数据类型:相关实现有 AtomicBoolean、AtomicInteger 和 AtomicLong,提供的方法主要有以下这些。newUpdater() 方法会抛出 IllegalArgumentException 这个运行时异常。原创 2024-07-29 08:15:00 · 326 阅读 · 0 评论 -
并发编程工具集——原子类(Compare And Swap)上(二十二)
getAndAddLong() 方法的实现,基本上就是 CAS 使用的经典范例。Java 提供的原子类里面 CAS 一般被实现为 compareAndSet(),compareAndSet() 的语义和 CAS 指令的语义的差别仅仅是返回值不同而已,compareAndSet() 里面如果更新成功,则会。简单的原子性问题,有两种方案: 方案一:互斥锁 方案二:无锁方案--原子类。并且返回 true;返回 true,否则返回 false。无锁方案的实现原理(硬件支持)CAS。无锁方案与互斥锁方案比较。原创 2024-07-24 08:30:00 · 559 阅读 · 0 评论 -
并发编程工具集——CountDownLatch和CyclicBarrier(下篇)(二十一)
CyclicBarrier 实现线程同步。原创 2024-07-24 08:00:00 · 241 阅读 · 0 评论 -
并发编程工具集——CountDownLatch和CyclicBarrier(上篇)(二十)
贯穿全文的主线业务逻辑:对账系统的处理逻辑是首先查询订单,然后查询派送单,之后对比订单和派送单,将差异写入差异库。用 CountDownLatch 实现线程等待。利用并行优化对账系统。原创 2024-07-15 10:23:07 · 299 阅读 · 0 评论 -
并发编程工具集——StampedLock-比ReadWriteLock更快的锁(下篇)(十九)
使用 StampedLock 一定不要调用中断操作,如果需要支持中断功能,一定使用可中断的悲观读锁 readLockInterruptibly() 和写锁 writeLockInterruptibly()。StampedLock 读模板:先乐观读读取,然后校验读取过程中,数值是否发生了变化,如果发生了变化,则加入悲观锁,再重新读取。StampedLock 使用注意事项。StampedLock 写模板。原创 2024-07-15 10:19:31 · 315 阅读 · 0 评论 -
并发编程工具集——StampedLock-比ReadWriteLock更快的锁(上篇)(十八)
StampedLock与ReadWriteLock的异同。ReadWriteLock支持两种:读锁、写锁。StampedLock 支持的三种锁模式。写锁、悲观读锁和乐观读。原创 2024-07-09 08:45:00 · 458 阅读 · 0 评论 -
并发编程工具集——读写锁-ReadWriteLock(下篇)(十七)
读锁调用 newCondition() 会抛出 UnsupportedOperationException 异常。原创 2024-07-09 08:00:00 · 251 阅读 · 0 评论 -
并发编程工具集——读写锁-ReadWriteLock(上篇)(十六)
快速实现一个缓存(用 ReadWriteLock 快速实现一个通用的缓存工具类。使用缓存首先要解决缓存数据的初始化问题。原创 2024-07-04 09:00:00 · 269 阅读 · 0 评论 -
并发编程工具集——限流器-Semaphore简述(十五)
Java SDK 里面提供了 Lock,为啥还要提供Semaphore。如何使用信号量(和可重入锁类似)原创 2024-07-04 08:30:00 · 509 阅读 · 0 评论 -
并发编程工具集——Lock和Condition(下)(十四)
当 RPC 结果返回时,会调用 doReceived() 方法,这个方法里面,调用 lock() 获取锁,在 finally 里面调用 unlock() 释放锁,获取锁后通过调用 signal() 来通知调用线程,结果已经返回,不用继续等待了。DefaultFuture 里面唤醒等待的线程,用的是 signal(),而不是 signalAll(),你来分析一下,这样做是否合理呢?,在 TCP 协议层面,发送完 RPC 请求后,线程是不会等待 RPC 的响应结果的。Dubbo帮我们做了异步转同步的事情,原创 2024-07-02 08:30:00 · 540 阅读 · 0 评论 -
并发编程工具集——Lock和Condition(中)(十三)
synchronized和Lock的区别。什么是可重入锁和可重入函数。原创 2024-07-02 08:15:00 · 305 阅读 · 0 评论 -
并发编程工具集——Lock和Condition(上)(十二)
Java SDK 里面的 ReentrantLock,内部持有一个 volatile 的成员变量 state,获取锁的时候,会读写 state 的值;volatile 变量规则:由于 state = 1 会先读取 state,所以线程 T1 的 unlock() 操作 Happens-Before 线程 T2 的 lock() 操作;传递性规则:线程 T1 的 value+=1 Happens-Before 线程 T2 的 lock() 操作。后续线程T2能够看到value的正确规则。原创 2024-06-28 09:00:00 · 599 阅读 · 0 评论 -
并发编程理论基础——面向对象与并发编程(十一)
有助于你写出“健壮”的并发程序的宏观原则。识别共享变量间的约束条件。原创 2024-06-28 08:30:00 · 336 阅读 · 0 评论 -
并发编程理论基础——合适的线程数量和安全的局部变量(十)
当你调用一个方法时,CPU 要先找到方法的地址,然后跳转到这个地址去执行代码,最后 CPU 执行完方法后返回。因为每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里面,不会共享,所以自然也就没有并发问题。注意:工作中都是按照逻辑核数来的,理论值和经验值仅供参考,实际上还是要靠压测。原创 2024-06-26 09:00:00 · 892 阅读 · 0 评论 -
并发编程理论基础——Java线程的生命周期(下)(九)
其中的 join() 是一种线程同步方法,例如有一个线程对象 thread A,当调用 A.join() 的时候,执行这条语句的线程会等待 thread A 执行完,而等待中的这个线程,其状态会从 RUNNABLE 转换到 WAITING。stop() 方法会真的杀死线程,如果线程持有 ReentrantLock 锁,被 stop() 的线程并不会自动调用 ReentrantLock 的 unlock() 去释放锁,那其他线程就再也没机会获得 ReentrantLock 锁。原创 2024-06-26 08:30:00 · 765 阅读 · 0 评论 -
并发编程理论基础——Java线程的生命周期(上)(八)
Java 语言里的线程本质上就是操作系统的线程,它们是一一对应的。这五种状态在不同编程语言里会有简化合并。原创 2024-06-24 09:00:00 · 455 阅读 · 0 评论 -
并发编程理论基础——管程(并发编程的万能钥匙)(七)
三个模型的区别:Hasen 模型、Hoare 模型和 MESA 模型的一个核心区别就是当条件满足后,如何通知相关线程。管程要求同一时刻只允许一个线程执行,那当线程 T2 的操作使线程 T1 等待的条件满足时,T1 和 T2 究竟谁可以执行呢?wait() 的正确姿势。原创 2024-06-24 08:45:00 · 477 阅读 · 0 评论 -
并发编程理论基础——安全性、活跃性以及性能问题【并发编程宏观角度来看会出现的问题】(六)
并发量:指的是能同时处理的请求数量,一般来说随着并发量的增加、延迟也会增加。所以延迟这个指标,一般都会是基于并发量来说的。例如并发量是 1000 的时候,延迟是 50 毫秒。性能问题(度量指标应该有一套完整的标准体系,过于空泛得不出好的性能方案,有些技术是好技术,但可能并不适用所有场景)吞吐量:指的是单位时间内能处理的请求数量。吞吐量越高,说明性能越好。延迟:指的是从发出请求到收到响应的时间。延迟越小,说明性能越好。原创 2024-06-18 09:00:00 · 615 阅读 · 0 评论