并发编程实战学习笔记(三)——基础构建模块

同步容器类的问题

  • 整个容器类加锁,线性访问容器实例,并发性能非常低
  • 虽然单个操作是线程安全的,但是复合操作如果不另外加锁,本身无法保证并发安全
  • 迭代器迭代过程中,如果发生元素的操作,会触发ConcurentModificationException异常,使用了“及时失败”机制
建议:通过并发容器来代替同步容器,可以极大地提高伸缩性并降低风险

ConcurrentHashMap的优化手段

  • 不是在每个方法上都在同一个锁上同步并使得每次只能有一线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程序的共享,这种机制称为分段锁。这种机制中,任意数量的读取线程可以并发地访问Map,执行读取操作的线程和执行写入操作的线程可以并发地访问Map,并且一定数量的写入线程可以并发地修改Map。
  • 迭代器不会抛出ConcurrentModificationException,因此不需要在迭代过程中对容器加锁。迭代器具有弱一致性,而并非“及时失败”。
  • 对于一些需要在整个Map上进行计算的方法,例如size/isEmpty,这些方法的语义被略微减弱了以反映容器的并发特性。
  • 添加了额外的复合原子操作【没有才插入、映射到值了才移除、映射到旧值才替换、映射到某个值时才替换到新值】

CopyOnWriteArrayList

原理

在每次修改时,都会创建并重新发布一个新的容器副本,从而实现可变性。

使用场景

每当修改容器时都会复制底层数组,这需要一定的开销,特别是容器的规模较大时。仅当迭代操作 远远 多于修改操作时,才应该使用“写入时复制”容器。

阻塞队列与生产-消费模式

问题

如果生产者生成工作的速率比消费者处理工作的速率快,那么工作项会在队列中累积起来,最终耗尽内存内存。

解决方法

阻塞队列提供了一个offer方法,如果数据项不能被添加到队列中,那么将返回一个失败状态。这样你就能创建更多的灵活策略来处理负荷过载的情况,例如将多余的工作项序列化并写入磁盘,减少生产者线程的数量,或者通过某种方式来抵制生产者线程。
在构建高可靠的应用程序时,有界队列是一种强大的资源管理工具:它们能抵制并防止产生过多的工作项,使应用程序在负荷过载的情况下变得更加健壮。

串行线程封闭

优点

对于可变对象,生产者-消费者这种设计与阻塞队列一起,促进了串行线程封闭,从而将对象所有权从生产者交付给消费者。

线程封闭对象只能由单个线程拥有,通过安全地发布该对象“转移”所有权,实现了转移前由前一线程独占,转移后由后一线程独占。

实现方法

阻塞队列使得这种线程封闭的所有权转移变得容易,其次还可以通过ConcurrentMap的原子方法remove或者AtomicReference的原子方法compareAndSet来完成这项工作。

闭锁(CountDownLatch)

使用场景

  • 确保某个计算在其需要的所有资源都被初使化之后才继续执行。
  • 确保某个服务在其依赖的所有其它服务都已经启动之后才启动。
  • 等待直到某个操作的所有参与者(如多玩家游戏中的所有玩家)都就绪再继续执行。

使用方法

可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初使化为一个正数,表示需要等待的事件数量。countDown方法递减计数器,表示一个事件已经发生了,而await方法等待计数器达到零,这表示所有需要等待的事件都已经发生。如果计数器的值非零,那么await会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。

FutureTask也可以用作闭锁

FutureTask表示的计算是通过callable来实现的,相当于一种可生成结果的Runnable,并且可以处于以下3种状态:等待运行、正在运行和运行完成。
执行完成表示计算的所有可能结束方式,包括正常结束、由于取消而结束和由于异常而结束等。
Future.get的行为取决于任务的状态。如果任务已经完成,那么get会立即返回结果,否则get会阻塞直到任务进入完成状态,然后返回结果或者抛出异常。

信号量的作用

计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个特定操作的数量。
你可以使用Semaphore将任何一种容器变成有界阻塞容器:信号量的计数值会初使化为容器容量的最大值。add操作在向底层容器添加一个元素之前,首先要获取一个许可。如果add操作没有添加任何元素,那么会立刻释放许可。同样,removew操作释放一个许可,使更多的元素能够添加到容器中。

栅栏

栅栏与闭锁的关键区别在于,所有线程必须都同时达到栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其它线程。

Exchanger

是一种两方栅栏,各方在栅栏位置上交换数据。如果一方通过exchange方法申请交换,而另一方还没有来,则会一直阻塞直至对方也提出申请。

最简单的方案是,当缓冲区被填满时,由填充任务进行交换,当缓冲区为空时,由清空任务进行交换。但是如果新数据的到达率不可预测,那么一些数据的处理过程就将延迟。另一个方法是,不仅当缓冲被填满时进行交换,并且当缓冲被填充到一定程序并保持一定时间后,也进行交换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值