java并发集合

it2023-09-29  90

集合是数据结构的系统实现.穿透的java集合大多都是属于"非线程安全的",虽然java追加了collector工具类实现集合的同步处理操作.但是其并发效率不高.所以为了更好的支持高并发任务处理.在JUC中提供了支持高并发的处理类.同时为了保证集合操作的一致性.这些高并发的集合类依然实现了集合标准接口.如List,Set,Map,Queue. 并发集合类的作用如下图: 1.传统集合进行多线程并发访问

/** * @Authro: QYF * @Time:2020/10/19 21:50 * 普通集合并发测试 */ public class JUCDemo1 { public static void main(String[] args) { List<String> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ for (int i1 = 0; i1 < 10; i1++) { list.add(Thread.currentThread().getName()); System.out.println(list); } },"普通集合并发操作"+i).start(); } } }

本程序通过循环产生10个线程.并且这10个线程对同一个集合进行数据保存与获取操作.而此程序一旦执行就会产生集合并发修改异常 java.util.ConcurrentModificationException 在ArrayList集合中为了防止多线程并发访问下的集合数据操作不准确特意定义有一个成员属性: 保存求模的数量:protected transient int modCount = 0; 在进行集合数据操作时还会提供一个expectedModCount变量,此变量的初始值为modCount.是指望这个集合表修改的次数.利用这两个数值就可以判断当前操作中是否出现并发访问问题,如果出现了并发访问错误,就会抛出并发修改异常. 另外需要提醒的是,JUC设计的核心思想在于,高性能的并发访问与安全修改.

2.并发单只集合类 CopyOnWriteArrayList十几元数组实现的并发集合访问类,在使用此类进行数据的"添加/修改/删除"操作时都会创建一个新的数组.并将更新后的数据复制到新建的数组中.由于每次更新操作都会创建新的数组,所以在进行数据修改时CopyOnWriteArrayList类的性能并不高,但是在进行数据遍历查找时性能会比较高.在使用Iterator进行迭代输出时不支持数据删除(remove方法)操作 CopyOnWriteArrayList采用复制和写入数组的形式完成,在源码中定义有以下成员属性:

private transient volatile Object[] array

在定义array数组时使用volatile定义了该对象数组,这样就可以保证直接对数据进行操作,同时为了保证安全的读/写操作,还提供了一个互斥锁的成员属性.

fianl transient Object lock = new Object();

在JDK新版本中并没有使用ReentrantLock而是定义了一个Object对象,在操作时利用synchronized进行锁定处理.这样在对数据进行"添加/删除/修改"操作时会先获取"互斥锁",当数据修改完毕后,先将数据更新到"volatile数组"中,然后再释放"互斥锁",以此实现数据保护的目的;

与CopyOnWriteArrayList类似的集合还有CopyOnWriteArraySet,它提供了一种无序的线程安全集合结构,可以理解为线程安全的HashSet实现,但是与HashSet区别在于,HashSet是基于散列方式存放的,而CopyOnWriteArraySet是基于数组(内部保证的是CopyOnWriteArrayList类)实现的. CopyOnWriteArraySet类的数据保存是依赖于CopyOnWriteArrayList类实现的,所以当使用Iteration输出时同样不支持删除操作.

ConcurrentHashMap是线程安全的哈希表实现类.在实现中它将哈希表许多的片段(Segment),每一个片段中除了保存有数据外还提供了一个可重用的"互斥锁",以片段的形式实现多线程的操作,即在同一个片段内多个线程访问是互斥的,而不同片段的访问采用是异步处理方式,这样使得ConcurrentHashMap在保证性能的前提下又可以实现数据的正确修改.

多线访问CopyOnWriteArraySet,ConcurrentHashMap,CopyOnWriteArrayList

public class JUCDemo2 { public static void main(String[] args) { //用法一样 // Set<String> set = new CopyOnWriteArraySet<>(); // Map<String,String> map= new ConcurrentHashMap<>(); List<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i < 10; i++) { new Thread(()->{ for (int i1 = 0; i1 < 10; i1++) { list.add(Thread.currentThread().getName()); System.out.println(list); } },"CopyOnWriteArrayList"+i).start(); } } }

3.调表集合 跳表是一种与平衡二叉树性能类似的数据结构,其主要是在有序链表上使用,在JUC提供的集合中有两个支持跳表操作的集合类型:ConcurrentSkipListMap,ConcurrentSkipLlistSet 跳表实现原理简介: 数组是一种常见的线性结构,如果在进行索引查询时其时间复杂度为O(1),但是在进行数据内容查询时,就必须基于有序存储并结合二分法进行查找,这样操作的时间复杂度为O(log2n).在多数情况下,数组由于其固定长度的限制,所以开发中会通过链表类解决.但是如果要想进一步提升链表的查询性能.就必须采用跳表结构来处理.而跳表结构的本质是需要提供有一个有序的链表集合.并从中依据二分法的原理抽取出一些样本数据.而后对样本数据的范围进行查询.

基于跳表并发测试

public class JUCDemo3 { public static void main(String[] args) throws InterruptedException { //同步处理,set同理 // Set<String> set=new ConcurrentSkipListSet<>(); CountDownLatch cd = new CountDownLatch(10); Map<String,Integer> map = new ConcurrentSkipListMap<>(); for (int i = 0; i < 100; i++) { new Thread(()->{ for (int k = 0; k < 10; k++) { map.put(Thread.currentThread().getName()+"["+k+"]",k); } //减少计数 cd.countDown(); },"集合操作线程"+i).start(); } //等待子线程 cd.await(); System.out.println(map.get("集合操作线程65[5]")); } }
最新回复(0)