Java并发编程艺术 6 Java并发容器和框架

来源:互联网 发布:超声波驱狗器 知乎 编辑:程序博客网 时间:2024/06/03 20:50
第六章 Java并发容器和框架

ConcurrentHashMap
ConcurrentHashMap是线程安全的HashMap,保证线程安全的同时保证高效。是HashMap和HashTable的进化版。

HashMap和HashTable的区别
HashMap和HashTable几乎是等价的,但还是存在一定的区别。
【1】HashMap不是线程安全的,HashTable是线程安全的。这也是最大的一个区别。
【2】HashMap可以支持null为键值对,HashTable不行。
【3】HashMap的迭代器是fail-fast迭代器,HashTable的迭代器不是fail-fast。 当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。
【4】因为HashTable是线程安全的,在单线程中HashMap的效率更好。
使用线程安全HashMap的方式,建议使用第二种。
1.Map m = Collections.synchronizeMap(hashMap);
2.使用ConcurrentHashMap

线程不安全的HashMap。
在多个线程(几十几百个)同时对HashMap进行put操作时,会引起HashMap的死循环。原因是多线程会导致HashMap的Entry链表形成环形数据结构。一旦形成环形数据额结构,Entry的next节点永远不为空,死循环获取next节点。
效率低下的HashTable
HashTable使用synchronized来保证线程安全,但在线程竞争激烈的情况下会导致效率极低。当每一个线程使用put时,其他线程的get、put等所有操作都会被阻塞。
ConcurrentHashMap的锁分段技术提高并发效率。
HashTable效率低是因为所有线程都竞争同一把锁。ConcurrentHashMap使用了锁分段技术,先将数据分成一段一段保存,每一段数据配一个锁。当一个线程访问一段数据时,其他的段的数据仍然能被访问。

ConcurrentHashMap由Segment数组和HashEntry数组组成。Segment是一个可重入的锁。
Segment保存在HashEntry的链表结构,对一个HashEntry操作时需要先获取对应的Segment锁。这就实现了分段锁。


使用线程安全的队列方式
1.使用阻塞算法的阻塞队列,通过入队和出队加锁的形式实现阻塞队列。
2.通过使用循环CAS的方式来实现

ConcurrentLinkedQueue:非阻塞的线程安全队列
ConcurrentLinkedQueue由head节点和tail节点组成,每个节点指向下一个节点。节点之间使用next进行关联,组成一张链表队列。
默认下head节点存储元素为空,tail等于head。

入队列:


出队列:



Java中的阻塞队列
Java中提供四种处理方式,七种阻塞队列的具体实现。
四种处理方式:抛出异常、返回特殊值、一直阻塞、超时退出。
【1】抛出异常:add(e)、remove()、element()三个方法分别是插入、移除、检测操作。如果队列已满,使用add会抛出异常illagelStateException。如果队列为空,使用remove操作会抛出NoSuchElementException。
【2】返回特殊值:offer(e)、poll()、peek()三个方法。插入成功返回true,移除方法是从队列取出一个元素,没有返回null
【3】一直阻塞:put()、take()两个方法。如果队列满,使用put方法,则线程阻塞,直到有其他线程取出了元素,使队列继续可用。take也是类似,如果队列为空,使用take则阻塞,直到队列有值。
阻塞式是比较常用的方式,一般是几个线程进行入队操作,几个线程出对操作,这样就可以实现不同线程之间的数据传递。
【4】超时方式offer(e,time,unit)、poll(time,unit)
注意:尽量不要使用无界队列(没有设置大小的队列,默认大小Integer.MAX-VALUE),这回导致put()方法不阻塞,
七种阻塞队列:前四种比较常用
AraayBlockingQueue:数组结构组成的有界阻塞队列
LinkedBlockingQueue:链表结构的有界阻塞队列
PriorityBlockingQueue:支持优先级的无界阻塞队列,小心导致低优先级任务饥饿
DelayQueue:使用优先级队列实现的无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列
LinkedTransferQueue:链表结构的无界阻塞队列
LinkedBlockingQueue:链表结构的双向阻塞队列

【1】AraayBlockingQueue:
数组结构组成的有界阻塞队列,使用了先进先出(FIFO)的原则。默认下不支持线程公平访问队列(非公平性:阻塞线程争夺线程访问资格,可能后阻塞的线程先访问,极端情况下导致某个线程永远阻塞。非公平性导致饥饿)。
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true)。设为true。线程公平。
【2】LinkedBlockingQueue
链表结构的有界阻塞队列。默认和最大长度Integer.MAX-VALUE。也是按照先入先出原则
【3】PriorityBlockingQueue
支持优先级的无界阻塞队列。默认下使用自然顺序升序。通过自定义类实现compareTo()方或者初始化时添加Comparator对元素进行排序。注意不能保证同优先级元素的顺序。
【4】DelayQueue
支持延时获取元素的无界阻塞队列。使用PriorityQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能在队列中获取。延迟期满以后才能获取
应用:
缓存系统设计:使用DelayQueue保存元素的有效期,使用线程循环检查DelayQueue,一旦能获取元素表示缓存有效期到了
定时任务调度:使用DelayQueue保存要定时执行的任务。比如TimerQueue就是用DelayQueue实现的
【5】:SynchronousQueue
【6】:LinkedTransferQueue
【7】:LinkedBlockingQueue
可以从两端进行插入和删除操作





原创粉丝点击