java并发编程实战笔记---(第五章)基础构建模块

.

 

5.1同步容器类

 

1.同步容器类的问题

复合操作,加容器内置锁

 

 

2.迭代器与concurrentModificationException

 

迭代容器用iterator, 迭代过程中,如果有其他线程修改容器,那么会抛出ConcurrentModificationException。迭代地方都需加锁

iterator是fail-fast及时失败机制。

解决方法:加锁可能死锁,复制容器性能有问题。

 

3.隐藏迭代器

加锁可以避免迭代抛出异常,那么所有对共享容器 迭代地方都需加锁。但是实际情况比较复杂 ,有时迭代器可能隐藏。

隐藏在字符串连接中的迭代操作。

编译器将字符串的连接转换为StringBuilder.append(Object),这个方法又会调用容器的tostring方法,标准容器的tostring方法会迭代容器。并在每个元素上调用tostring来生产容器内容的格式化表示。 

 

 *******下面的话

 

 

5.2 并发容器

同步容器将所有对容器状态的访问都串行化,当多个线程竞争容器的锁时,吞吐量严重降低。

并发容器针对多线程访问设计,并发容器代替同步容器可提高伸缩性并降低风险。

 

queue

ConcurrentLinkedQueue是传统fifo队列,PriorityQueue是非并发的优先队列。

BlockingQueue增加了可阻塞的插入和获取。ConcurrentHashMap并发map,concurrentSkipList.set

 

1.ConcurrentHashMap

同步容器等待时间可能较长,效率较低。

ConcurrentHashMap使用分段锁机制,使得多个线程可以同时正删改容器内容。效率较高。

ConcurrentHashMap与其他并发容器增强了同步容器的迭代器,不会抛出ConcurrentModificationException,不用加锁迭代。弱一致性机制(weakly consistent),而非fail-fast。

size,isEmpty功能弱化了,结果可能不精确,主要确保remove,get,put,containsKey等功能性能优化。

一般使用ConcurrentHashMap,只有需要对容器进行独占访问时,采用HashTable或者SynchronizedMap.

 

2.ConcurrentMap额外的原子Map操作

 

3.CopyOnWriteList

CopyOnWriteList代替同步list,有时有更好的并发性能,而且迭代时不用加锁或复制。

 写入时复制:只要发布一个事实不可变的对象,

 

 

 每当修改会复制,消耗性能,所以当迭代操作远远多余修改操作时使用。

适用于事件通知系统,遍历已注册监听器列表的操作较多,增加或者移除监听器操作相对较少。

 

5.3阻塞队列 消费者-生产者模式

1.阻塞队列提供可阻塞的put,take方法,可定时的offer,poll方法,队列可有界,可无界。

BlockingQueue简化了生产者消费者模式的实现,支持任意数量的生产者和消费者。最常见生消举例:线程池和工作队列的组合。

有时需要调整生产者消费者的比率。offer可返回操作成败,以判断是否需要修改调整负载。有界队列可以防止产生过多的生产,避免负载过高。

需要尽早设计有界队列或者通过信号量semaphore设计资源管理机制。

 

BlockingQueue有多种实现,LinkedBlockingQueue,ArrayBlockingQueue,PriorityBlockingQueue,SynchronousQueue没有缓冲,生产一个消费一个,否则一直阻塞,或者说容量就是1.应该适用于单生产单消费。

实例:桌面搜索

将工作内容进行拆分:查找文件为生产者的工作,建立索引为消费者的工作。

2.串行线程封闭

 

 

 

 

3.双端队列,工作密取

适用于既是生产者又是消费者,自产自消,(递归)执行过程发现更多的任务,往自己队列加任务。

 

 

 5.4阻塞方法和中断方法

 线程阻塞或暂停执行:等待io操作完成,等待获取某个锁,等待Thread.sleep中醒来,等待其他线程的计算结果等等。

 

 

 

 

 5.5同步工具类

同步工具类:根据自身的状态控制线程的执行流。阻塞队列,信号量(Semaphore),栅栏(Barriar),闭锁(Latch).

1.闭锁 (相当于一扇门)

线程等到某个状态或者时刻才可以继续执行

 

new countdownlatch(N)

countdown()

await()

 让一定数量线程等待N个事件处理完成才继续操作。

 

 

 

 

 

 起始门使得主线程同时释放所欲执行线程,结束门使得主线程等待所有执行线程结束。

 一种倒计数锁存器,如上例。

另一种典型用法是,将一个问题分成 N 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。当所有的子部分完成后,协调线程就能够通过 await。(当线程必须用这种方法反复倒计数时,可改为使用 CyclicBarrier。)  

 

 2.FutureTask

get在获取到结果之前一直处于阻塞状态,实现了runnable接口。 继承future

 

 

 

 

 

 3.信号量 (数量)

控制同时访问某个特定资源的操作数量,或者同时执行某个操作的数量。计数信号量可以实现某种资源池,或者对容器施加边界。

 

 

 

 

 

 

 4.栅栏

 

 

 

 

 

 

 5.6构建高效且可伸缩的结果缓存

 

1.用concurrenthashmap  可以并发操作,不像加锁效率低

2.用FutureTask  当一个计算时间较长时,另一个线程等待拿到结果

3.用putIfAbsent  if判断不能确保原子性 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2017-06-06 17:57  上台阶  阅读(328)  评论(0编辑  收藏  举报