如果要进行多个数据的保存,无疑首选类集(List、Set、Queue、Map),在类集的学习的时候也知道一个概念:许多集合的子类都具有同步与异步的差别,但是如果真的要在多线程之中去使用这些类,是否真的可以用呢?

范例:观察一下类集的问题:

package so.strong.mall.concurrent;
import java.util.ArrayList;
import java.util.List; public class ListDemo {
public static void main(String[] args) {
final List<String> all = new ArrayList<>();
for (int i = 0; i < 20; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 20; j++) {
all.add(Thread.currentThread().getName() + "-" + temp + "-" + j);
System.out.println(all);
}
}
}).start();
}
}
}

不过很遗憾的是这个时候以上的代码出现了“java.util.ConcurrentModificationException”,该异常主要指的是当保存的容量个数和实际操作数可能不匹配的时候就出现该异常。

并发集合类

为了更好的实现集合的高并发访问处理,juc中创建一组新的集合工具类:

List和Set集合

public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

  CopyOnWriteArrayList相当于线程安全的ArrayList,它实现了List接口。CopyOnWriteArrayList是支持高并发的。

public class CopyOnWriteArraySet<E> extends AbstractSet<E> implements java.io.Serializable

  相当于线程安全的HashSet,它继承与AbstractSet类。

  CopyOnWriteArraySet内部包含一个CopyOnWriteArrayList对象,它是通过CopyOnWriteArrayList来实现的。

Map集合

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable 

  ConcurrentHashMap是线程安全的哈希表,相当于线程安全的HashMap,它继承与AbstractMap类,并且实现了ConcurrentMap接口。ConcurrentHashMap是通过“锁分段”来实现的,它支持并发;

public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V> implements ConcurrentNavigableMap<K,V>,Cloneable,java.io.Serializable

  ConcurrentSkipListMap是线程安全的有序哈希表(相当于线程安全的TreeMap),它继承于AbstractMap类,并且实现了ConcurrentNavigableMap接口。ConcurrentSkipListMap是通过“跳表”来实现的,它支持并发;

public class ConcurrentSkipListSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable

  ConcurrentSkipListSet是线程安全的有序的集合(相当于线程安全的TreeSet);它继承了AbstractSet,并且实现了NavigableSet接口。ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,它也支持并发。

Queue队列

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable

  ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列;

public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable

  LinkedBlockingQueue是单向链表实现的(指定大小)阻塞队列,该队里按FIFO先进先出排序元素;

public class LinkedBlockingDeque<E>extends AbstractQueue<E> implements BlockingDeque<E>,  java.io.Serializable

  LinkedBlockingDeque是双向链表实现的(指定大小)双向并发阻塞队列,该阻塞队列同时支持FIFO和FILO两种操作方式;

public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable

  ConcurrentLinkedQueue是单向链表实现的无界队列,该队列按FIFO排序元素;

public class ConcurrentLinkedDeque<E> extends AbstractCollection<E> implements Deque<E>, java.io.Serializable

  ConcurrentLinkedDeque是双向链表的无界队列,该阻塞队列同时支持FIFO和FILO两种操作方式。

单值并发集合

juc包里面提供的CopyOnWriteArrayList、CopyOnWriteArraySet很明显是针对于List和Set接口实现的子类。

范例:使用CopyOnWriteArrayList实现多线程访问

package so.strong.mall.concurrent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; public class ListDemo {
public static void main(String[] args) {
final List<String> all = new CopyOnWriteArrayList<>();
for (int i = 0; i < 20; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 20; j++) {
all.add(Thread.currentThread().getName() + "-" + temp + "-" + j);
System.out.println(all);
}
}
}).start();
}
}
}

范例:使用CopyOnWriteArraySet实现多线程访问

package so.strong.mall.concurrent;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; public class ListDemo {
public static void main(String[] args) {
final Set<String> all = new CopyOnWriteArraySet<>();
for (int i = 0; i < 20; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 20; j++) {
all.add(Thread.currentThread().getName() + "-" + temp + "-" + j);
System.out.println(all);
}
}
}).start();
}
}
}

如果某一个类需要存储用户的公共资源,并且多个线程允许同时写入数据的话,就可以考虑使用此类集合实现处理。

ConcurrentHashMap

观察继承结构:

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
implements ConcurrentMap<K, V>, Serializable

发现它是ConcurrentMap接口的子类,而ConcurrentMap接口定义如下:

public interface ConcurrentMap<K, V> extends Map<K, V>

范例:默认情况下使用ConcurrentHashMap的基本使用:

package so.strong.mall.concurrent;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapDemo {
public static void main(String[] args) {
final Map<String, String> all = new ConcurrentHashMap<>();
for (int i = 0; i < 2; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 3; j++) {
all.put(Thread.currentThread().getName(), "x = " + temp + ",j = " + j);
System.out.println(all);
}
}
}).start();
}
}
}

  使用ConcurrentHashMap并不仅仅是去解决“java.util.ConcurrentModificationException”异常,Map集合的主要特征是做数据的查询操作,所以在ConcurrentHashMap设计的时候考虑到了数据更新的安全性和数据查询的并发性。

ConcurrentHashMap的整体特征:写的时候同步写入,使用独占锁,读的时候为了保证性能使用了共享锁。

跳表(Skip)集合

跳表集合本质上的功能是一种快速查询功能,也就是说它会在一个有序的链表里面选择一些数据作为检索的种子数,那么利用这些种子数方便进行数据的查找,非常类似于二分法。

在juc开发包里面提供有跳表的多线程支持程序类:ConcurrentSkipListMap、ConcurrentSkipListSet。

范例:观察跳表实现ConcurrentSkipListMap

package so.strong.mall.concurrent;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import sun.java2d.pipe.SpanIterator;
import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap; public class SkipDemo {
public static void main(String[] args) {
final Map<String, String> all = new ConcurrentSkipListMap<>();
for (int i = 0; i < 20; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 30; j++) {
all.put(Thread.currentThread().getName(), "x=" + temp + ",j=" + j);
}
}
}).start();
}
System.out.println(all.get("Thread-0"));
} }

范例:使用ConcurrentSkipListSet

package so.strong.concurrents;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet; public class SkipDemo {
public static void main(String[] args) {
final Set<String> all = new ConcurrentSkipListSet<>();
for (int i = 0; i < 2; i++) {
final int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
all.add(Thread.currentThread().getName()+"-"+temp+"-"+j);
}
}
}).start();
}
System.out.println(all.contains("Thread-0-0-1"));
}
}

如果要想保证快速地定位查询,那么使用跳表是最快速的,因为其检索的算法要比顺序检索强许多。

JUC——并发集合类的更多相关文章

  1. JUC并发集合类CopyOnWriteList

    CopyOnWriteList简介 ArrayList是线程不安全的,于是JDK新增加了一个线程并发安全的List--CopyOnWriteList,中心思想就是copy-on-write,简单来说是 ...

  2. JUC并发编程学习笔记

    JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...

  3. JUC并发编程与高性能内存队列disruptor实战-上

    JUC并发实战 Synchonized与Lock 区别 Synchronized是Java的关键字,由JVM层面实现的,Lock是一个接口,有实现类,由JDK实现. Synchronized无法获取锁 ...

  4. 多线程JUC并发篇常见面试详解

    @ 目录 1.JUC 简介 2.线程和进程 3.并非与并行 4.线程的状态 5.wait/sleep的区别 6.Lock 锁(重点) 1.Lock锁 2.公平非公平: 3.ReentrantLock ...

  5. Java 理论与实践: 并发集合类

    Java 理论与实践: 并发集合类 DougLea的 util.concurrent 包除了包含许多其他有用的并发构造块之外,还包含了一些主要集合类型 List 和 Map 的高性能的.线程安全的实现 ...

  6. JAVA Concurrent包 中的并发集合类

    我们平时写程序需要经常用到集合类,比如ArrayList.HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行 ...

  7. 多线程进阶——JUC并发编程之CountDownLatch源码一探究竟

    1.学习切入点 JDK的并发包中提供了几个非常有用的并发工具类. CountDownLatch. CyclicBarrier和 Semaphore工具类提供了一种并发流程控制的手段.本文将介绍Coun ...

  8. JUC并发编程基石AQS之主流程源码解析

    前言 由于AQS的源码太过凝练,而且有很多分支比如取消排队.等待条件等,如果把所有的分支在一篇文章的写完可能会看懵,所以这篇文章主要是从正常流程先走一遍,重点不在取消排队等分支,之后会专门写一篇取消排 ...

  9. JUC 并发类概览

    JUC 并发类及并发相关类概览,持续补充... AQS 内部有两个队列,一个等待队列(前后节点),一个条件队列(后继节点),其实是通过链表方式实现: 等待队列是双向链表:条件队列是单向链表:条件队列如 ...

随机推荐

  1. docker 部署django项目(nginx + uwsgi +mysql)

    最近在学习用docker部署Django项目,经过百折不挠的鼓捣,终于将项目部署成功,爬过好多坑,也发现很多技能需要提高.特此写下随笔与小伙伴们分享,希望能对大家有所启发. docker的理论我就不赘 ...

  2. 【python库安装问题解决】UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 121: invalid start byte

    好久没用python了...今天随便pip安装个库突然报错: Exception:‘’ (most recent call last):  File "C:\ProgramData\Anac ...

  3. python第十四课--排序及自定义函数之自定义函数(案例三)

    return关键字的使用:1).结束函数 2).将结果返回给函数的调用者/调用处 [注意事项]1).与return同一作用范围内的后面不要显示书写任何代码,因为永远不可能被执行到,不会报错. 2).r ...

  4. python第九课——while死循环

    2.3.无限循环/死循环: 何时发生无限循环? 循环条件永远为True,就出现了无限循环 [注意] 无限循环是需要避免的,因为它极其占用系统资源: 但是配合我们之后讲的break等关键字,就会变得更有 ...

  5. ES6新特性1:let和const

    本文摘自ECMAScript6入门,转载请注明出处. 一.let 1. ES6增了let命令,用来声明变量.它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效. { let a ...

  6. 1349: Taking Pebbles (博弈 打表找规律)

    1349: Taking Pebbles Submit Page    Summary    Time Limit: 1 Sec     Memory Limit: 128 Mb     Submit ...

  7. GCD死锁,及同步、异步、串行和并行队列组合情形

      一.概述 1)队列用来存储代码任务,线程用来运行代码任务: 2)main()函数作为程序入口,整个程序默认运行在主线程中,程序代码任务默认存放在主队列中: 3)以下所谓阻塞线程是针对主线程而言(子 ...

  8. iOS 11 使用方法替换(Method Swizzling),去掉导航栏返回按钮的文字

    方法一:设置BarButtonItem的文本样式为透明颜色,代码如下: [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegro ...

  9. chrome浏览器使用jqprint插件打印时偶尔空白页问题

    最近测试老是提bug说是有50%的概率打印出空白页,之前我也一直发现偶尔会出现这个问题,只是一直没有发现原因. 今天终于下定决心找到问题所在.开始吧! 查看源码一行行debug,发现问题只可能出现在这 ...

  10. html5的文档申明为什么是<!DOCTYPE html>?

    首先我们来了解一下什么是文档声明: 文档声明就是文档告诉游览器该以什么样的标准去解析它.游览器可以解析的文档可不止html,还有xhtml,xml...当然在这里我们并不需要知道xhtml.xml是什 ...