线程安全的集合容器
List集合中的线程安全的集合容器:
在旧版本中Vector是线程安全的集合容器,在JDK 1.5以后CopyOnWriteArrayList也是线程安全的集合容器,CopyOnWriteArrayList的数据结构是Object类型的数组。
CopyOnWriteArrayList是如何实现线程安全的:通过 “ReentrantLock锁” 实现线程安全,写入操作(添加、删除、修改元素)使用同一个Lock对象进行线程安全的同步管理,采用COW思想(CopyOnWrite);因为CopyOnWriteArrayList集合在扩容时实在原数组基础上加一进行扩容,就是添加一个元素扩容一下,当它进行扩容时,会将原数组进行复制到一个新数组中进行添加元素的操作,所以不会影响集合的读取;读写分离:线程读取集合元素的同时,其它线程可以同时写入
Set集合中的线程安全的集合容器:
CopyOnWriteArraySet: CopyOnWriteArraySet的底层就是CopyOnWriteArrayList,但在此基础上添加了去重方法
Queue(队列):
队列的特点:FIFO先进先出
BlockingQueue阻塞队列:
特点:在队列的基础上,使用两个不同的线程进行队列操作
- 读取线程负责读取队列元素,进行出队操作,当队列为空时,读取线程阻塞;发生出队操作后,唤醒阻塞中的写入线程;
- 写入线程负责写入队列元素,进行入队操作,当队列为满时,写入线程阻塞;发生入队操作后,唤醒阻塞中的读取线程;
实现类
-
有界队列:ArrayBlockingQueue
数据结构:Object类型的数组
因为ArrayBlockingQueue是有Object数组实现的并且在ArrayBlockingQueue只有有参构造方法,参数是队列的大小
线程的同步:通过一个ReentrantLock锁实现(写入的同时,不允许读取)
通过观察源码可以得知:在ArrayBlockingQueue的put()方法中,当队列的元素个数等于数组长度会使put线程进入阻塞状态(并不是真正意义上的阻塞相当于等待状态)小案例:
package Threadday4;import java.util.concurrent.ArrayBlockingQueue;public class Test04 {public static void main(String[] args) throws InterruptedException {// 基于Object数组实现的有界阻塞队列ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<String>(5);for (int i = 1; i <= 100; i++) {//入队操作可以使用put方法 // arrayBlockingQueue.offer("A" + i);arrayBlockingQueue.put("S" + i);if (i % 5 == 0) {//出队操作可以使用take方法 // System.out.println("出队元素:"+arrayBlockingQueue.poll());System.out.println("出队元素:"+arrayBlockingQueue.take());}System.out.println(arrayBlockingQueue);}} }
-
无界队列:LinkedBlockingQueue
数据结构:单向链表
无界队列的无界只是相对意义上的无界
线程同步:通过两个ReentrantLock锁实现(写入操作使用putLock,读取操作使用takeLock)
小案例:
package Threadday4;import java.util.concurrent.LinkedBlockingQueue;public class Test05 {public static <E> void main(String[] args) throws InterruptedException {// 基于单项列表实现的无届队列(无界)// 它的线程同步是通过两个ReentrantLock锁实现的LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();for (int i = 1; i <= 100; i++) {//入队操作可以使用put方法queue.put("S" + i);if (i % 5 == 0) {//出队操作可以使用take方法System.out.println("出队元素:"+queue.take());}System.out.println(queue);}} }
-
延迟队列:DelayedWorkQueue
队列中的元素按照延迟时间排序
-
优先队列:PriorityBlockingQueue
队列中的元素按照指定的优先级排序
小案例:使用一个简单的订单说明
OrederPayMent类
package Threadday4;import java.math.BigDecimal; import java.util.UUID;public class OrederPayMent implements Runnable ,Comparable<OrederPayMent>{private int orderNo; // 订单编号private String trackId; // 支付流水号private BigDecimal payment; // 支付金额public OrederPayMent(int orderNo, BigDecimal payment) {trackId = UUID.randomUUID().toString().substring(0, 10) + System.currentTimeMillis();this.orderNo = orderNo;this.payment = payment;}public int getOrderNo() {return orderNo;}public void setOrderNo(int orderNo) {this.orderNo = orderNo;}public String getTrackId() {return trackId;}public void setTrackId(String trackId) {this.trackId = trackId;}public BigDecimal getPayment() {return payment;}public void setPayment(BigDecimal payment) {this.payment = payment;}@Overridepublic void run() {System.out.printf("订单编号为:%d的订单,完成支付,支付金额为:¥%s,支付流水号为:%s\n", orderNo, payment, trackId);}@Overridepublic int compareTo(OrederPayMent o) {return o.payment.compareTo(this.payment);}}
根据自定义的优先级规则(价钱)实现Comparable接口
package Threadday4;import java.math.BigDecimal; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;public class Test06 {public static void main(String[] args) {//创建线程池ThreadPoolExecutor pool = new ThreadPoolExecutor(0, 2, 0, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());for(int i = 0; i<=100;i++) {pool.execute(new OrederPayMent(i, new BigDecimal(String.valueOf(Math.random()*100))));}} }
Map集合中线程安全的集合容器:
Hashtable:数据结构:数组加链表因为hashtable的方法中是为synchronized关键字锁修饰的所有线程安全但是性能差
ConcurrentHashMap:
数据结构:Node[ ]数组 + 链表 + 红黑树
在JDK1.7版本中,ConcurrentHashMap是通过分段锁来实现线程安全的,分段锁:是将数组划分成若干不同的"区域",每个区域使用一把锁来控制线程安全;在JDK1.8版本以后,ConcurrentHashMap是通过synchronized同步锁 + CAS来实现线程安全的,就是在多线程并发时,产生Hash冲突,必须竞争同一把锁(链表的头节点或红黑树的根节点)
]数组 + 链表 + 红黑树
在JDK1.7版本中,ConcurrentHashMap是通过分段锁来实现线程安全的,分段锁:是将数组划分成若干不同的"区域",每个区域使用一把锁来控制线程安全;在JDK1.8版本以后,ConcurrentHashMap是通过synchronized同步锁 + CAS来实现线程安全的,就是在多线程并发时,产生Hash冲突,必须竞争同一把锁(链表的头节点或红黑树的根节点)