使用synchronized的集合、CopyOnWriteArrayList集合、CopyOnWriteArraySet集合、ConcurrentHashMap集合、Queue接口
14.5 线程安全的集合
14.5.1 线程安全集合图
- Collection体系集合下,除Vector以外的线程安全集合;
14.5.2 Collections中的工具方法
Collections工具类中提供了多个可以获得线程安全集合的方法;
- public static Collection synchronizedCollection(Collection
c) - public static List synchronizedList(List list)
- public static Set synchronizedSet(Set s)
- public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
- public static SortedSet synchronizedSortedSet(SortedSet s)
- public static <K,V> SortedMap<K,V>
- synchronizedSortedMap(SortedMap<K,V> m)
JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchronized实现;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TestCollectionsForSyn {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//传入一个线程不安全的List,返回一个线程安全的List
List<String> safeList = Collections.synchronizedList(list);
//和普通集合应用无差别
safeList.add("A");//SynchronizedList里的add方法,该方法里加了个锁
safeList.add("B");
safeList.add("C");
safeList.remove(1);
safeList.get(1);
Set<String> set = new HashSet<String>();
Set<String> newSet = Collections.synchronizedSet(set);
newSet.add("A");
newSet.add("B");
newSet.add("C");
for(String s : newSet) {
System.out.println(s);
}
}
}
14.5.3 CopyOnWriteArrayList
- 线程安全的ArrayList,加强版读写分离;
- 写有锁,读无锁,读写之间不阻塞,优于读写锁;
- 写入时,先copy一个容器副本、再添加新元素,最后替换引用;
- 使用方法与ArrayList无异;
14.5.4 CopyOnWriteArraySet
- 线程安全的Set,底层使用CopyOnWriteArrayList实现;
- 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组;
- 如存在元素,则不添加(扔掉副本);
14.5.5 ConcurrentHashMap
- 初始容量默认为16段(Segment),使用分段锁设计;
- 不对整个Map加锁,而是为每个Segment加锁;
- 当多个对象存入同一个Segment时,才需要互斥;
- 最理想状态为16个对象分别存入16个Segment,并行数量16;
- 使用方式与HashMap无异;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class TestCopyOnWriteArrayList {
public static void main(String[] args) {
//写有锁,读无锁的集合
CopyOnWriteArrayList<String> alist = new CopyOnWriteArrayList<String>();
//写操作
alist.add("A");//都将底层数组做了一次复制,写的是新数组,完成赋值后,再将新数组替换掉旧数组
alist.add("B");//没调用一次,底层方法扩容一次
//读操作,无锁
alist.get(1);//读的是写操作完成之前的旧数组,写完之后,才能读到新数组的新值
//无序,无下标,不允许重复
CopyOnWriteArraySet<String> aset = new CopyOnWriteArraySet<String>();
//写操作,表面使用的是add方法;底层实际是用的CopyOnWriteArrayList的addIfAbsent()来判断要插入的新值是否存在
aset.add("A");
aset.add("B");
aset.add("C");
for(String s : aset) {
System.out.println(s);
}
HashMap<String , String> maps = new HashMap<String , String>();
//分段锁设计 Segment JDK1.7的做法
ConcurrentHashMap<String , String> ch = new ConcurrentHashMap<String , String>();
//1.8的做法 CAS交换算法和同步锁 同步锁锁的是表头对象(链表的第一个),拿到锁的对象要先做节点遍历,查看有没有相同的key,相同覆盖,不同,则挂在最后一个节点的next上
ch.put("A", "1");
ch.keySet();
}
}
14.5.6 Queue接口(队列)
- Collection的子接口,表示队列FIFO(First In First Out);
常用方法:
- 抛出异常:
boolean add(E e) //顺序添加一个元素(到达上限后,再添加则会抛出异常)
E remove() //获得第一个元素并移除(如果队列没有元素时,则抛出异常)
E element() //获得第一个元素但不移除(如果队列没有元素时,则抛出异常) - 返回特殊值:推荐使用
boolean offer(E e) //顺序添加一个元素(到达上限后,再添加则会返回false)
E poll() //获得第一个元素并移除(如果队列没有元素时,则返回null)
E keep() //获得第一个元素但不移除(如果队列没有元素时,则返回null)
14.5.7 ConcurrentLinkedQueue
- 线程安全、可高效读写的队列,高并发下性能最好的队列;
- 无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N);
- V : 要更新的变量、E:预期值、N:新值;
- 只有当V == E 时,V = N ; 否则表示已被更新过,则取消当前操作;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class TestQueue {
public static void main(String[] args) {
//Queue
//列表,尾部添加(指定下标添加)
//链表,头尾添加
//队列,FIFO
//Queue<String> qu = new LinkedList<String>();//遵循队列规则的链表
LinkedList<String> link = new LinkedList<String>();
link.offer("A");
link.offer("B");
link.offer("C");
//用列表的方式打乱了FIFO队列
link.add(0,"D");
//强制LinkedList后,不能调用带有下标的add方法
System.out.println(link.peek());//队列中的第一个元素
//严格遵守理论队列的规则,且是线程安全的,采用了CAS交换算法
Queue<String> q = new ConcurrentLinkedQueue<String>();
//1.抛出异常的 2.返回结果的
q.offer("A");
q.offer("B");
q.offer("C");
q.poll();//删除表头
System.out.println(q.peek());//获得表头
}
}
14.5.8 BlockingQueue接口(阻塞队列)
- Queue的子接口,阻塞的队列,增加了两个线程状态为无期限等待的方法;
方法:
- void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待;
- E take() // 获取并移除此队列头部元素,如果没有可用元素,则等待;
- 可用于解决生产者、消费者问题;
14.5.9 阻塞队列
-
ArrayBlockingQueue:
数组结构实现,有界队列(手工固定上限) -
LinkedBlockingQueue:
链表结构实现,无界队列(默认上限Integer.MAX_VALUE)
14.6 总结
- ExecutorSevice线程池接口、Executors工厂;
- Callable线程任务、Future异步返回值;
- Lock、ReentrantLock重入锁、ReentrantReadWriteLock读写锁;
- CopyOnWriteArrayList线程安全的ArrayList;
- CopyOnWriteArraySet线程安全的Set;
- ConcurrentHashMap线程安全的HashMap;
- ConcurrentLinkedQueue线程安全的Queue;
- ArrayBlockingQueue线程安全的阻塞Queue;(生产者、消费者)