您当前的位置:网站首页>言情小说,Java同步容器和并发容器,guess是什么牌子

言情小说,Java同步容器和并发容器,guess是什么牌子

2019-04-20 01:16:31 投稿作者:admin 围观人数:297 评论人数:0次

同步容器

在 Java 中,同步容器首要包含 2 类:

  • Vector、Stack、HashTableCollections 类中供给的静态工厂办法创立的类(由 Collections.synchronizedXxxx 等办法)
  • Vector 完成了 List 接言情小说,Java同步容器和并发容器,guess是什么牌子口,Vector 实际上便是一个数组,和 ArrayList 类似,可是 Vector 中的办法都是 synchronized 办法,即进行了同步办法。
  • Stack 也是一个同步容器,它的办法也用 synchronized 进行了同步,它实际上是承继于 Vector 类。
  • HashTable 完成了 Map 接口,它和 HashMap 很类似,可是 HashTable 进行了同步处理,而 HashMap 没有。

同步容器的缺点

同步容器的同步原理便是在办法上用 synchronized 润饰。那么,这些办法每次只允许一个线程调用履行。

功能问题

由于被 synchronized 润饰的办法,每次只允许一个线程履行,其他企图拜访这个办法的线程只能等候。明显,这种办法比没有运用 synchronized 的容器功能要差。

安全问题

同步容器真的必定安全吗?

答案是:未必。同步容器未必真的安全。在做复合操作时,依然需求加锁来维护。

常见复合操作如下:

  • 迭代:重复拜访元素,直到遍历完悉数元素;
  • 跳转:依据指定次序寻觅当时元素的下一个(下 n 个)元素;
  • 条件运算:例如若没有则增加等;

不安全的示例

public class Test { static Vector vector = new Vector();
public static void main(String[] args) throws InterruptedException {
while(true) {
for (int i=0;i<10;i++)
vector.add(i);
Thread thread1 = new Thread(){
public void run() {
for (int i=0;i vector.remove(i);
}
;
}
;
Thread thread2 = new Thread(){
public void run() {
for (int i=0;i vector.get(i);
}
;
}
;
thread1.start();
thread2.start();
while(Thread.activeCount()>10) {
}
}
}
}

履行时或许会呈现数组越界过错。

Vector 是线程安全的,为什么还会报这个错?很简单,关于 Vector,尽管能确保每一个时刻只能有一个线程拜访它,可是不扫除这种或许:

当某个线程在某个时刻履行这句时:

for (int i=0;i vector.get(i);

假若此刻 vector 的 size 办法回来的是 10,i 的值为 9

然后别的一个线程履行了这句:

for (int i=0;i vector.remove(i);

将下标为 9 的元素删去了。

那么经过 get 办法拜访下标为 9 的元素必定就会出问题了。

安全示例

因而为了确保线程安全,必须在办法调用端做额定的同步办法,如下面所示:

public class T狐妖小红娘之尘雅缘est {
static Vector vector = new Vector();
public static void main(String[] args) throws InterruptedException {
while(true) {
for (int i=0;i<10;i++)
vector.add(i);
Thread thread1 = new Thread(){
public void run() {
synchronized (Test.class) {
//进行额定的同步
for (int i=0;i vector.remove(i);
}
}
;
}
;
Thread thread2 = new Thread(){
public void run() {
synchronized (Test.class) {
for (int i=0;i vector.get(i);
}
}
;
}
;
thread1.start();
thread2.start();
while(Thread.activeCount()>10) {
}
}
}
}

ConcurrentM言情小说,Java同步容器和并发容器,guess是什么牌子odificationException 反常

在对 Vector 等容器并发地进行迭代修正时,会报 ConcurrentModificationException 反常,关于这个反常将会在后续文章中叙述。

可是在并发容器中不会呈现这个问题。

并发容器

JDK 的 j速尔ava.util.concurrent 包(即 juc)中供给了几个十分有用的并发容器。

  • CopyOnWriteArrayList - 线程安全的 ArrayList
  • CopyOnWriteArraySet - 线程安全的 Set,它内部包含了一个 CopyOnWriteArrayList,因而本质上是由 CopyOnWriteArrayList 完成的。
  • ConcurrentSkipListSet - 相当于线程安全的 TreeSet。它是有序的 Set。它由 ConcurkeyrentSkipListMap 完成。
  • ConcurrentHashMap - 线程安全的 HashMap。选用分段锁完成高效并发。
  • ConcurrentSkipListMap - 线程安全的有序 Map。运用跳表完成高效并发。
  • ConcurrentLinkedQueue - 线程安全的无界行列。底层选用单链表。支撑 FIFO。
  • ConcurrentLinkedDeque - 线程安全的无界双端行列。底层选用双向链表。支撑 FIFO 和 FILO。
  • ArrayBlockingQueue - 数组完成的堵塞行列。
  • LinkedBlockingQueue - 链表完成的堵塞行列。
  • LinkedBlockingDeque - 双向链表完成的双端堵塞行列。

ConcurrentHashMap

关键

  • 效果:ConcurrentHashMap 是线程安全的 HashMap。
  • 原理:JDK6 与 JDK7 中,ConcurrentHashMap 选用了分段锁机制。JDK8 中,摒弃了锁分段机制,改为运用 CAS 算法。

源码

JDK7

ConcurrentHashMap 类在 jdk1.7 中子洲醉汉的规划,其根本结构如图所示:

每一个 segment 都是一个 HashEntry[] table, table 中的每一个元素本质上都是一个 HashEntry 的单向行列。比方 table[3]为首节点,table[3]->next 为节点 1,之后为节点 2,顺次类推。

public class ConcurrentHashMap extends AbstractMap
implements ConcurrentMap, Serializable { // 将整个hashmap分红几个小的map,每个segment都是一个锁;与hashtable比较,这么规划的意图是关于put, remove等操作,能够削减并发抵触,对 // 不属于同一个片段的节点能够并发操作,大大提高了功能
final Segment[] segments;
// 本质上Segment类便是一个小的hashmap,里边table数组存储了各个节点的数据,承继了ReentrantLock, 能够作为互拆锁运用
static final class Segment extends ReentrantLock implements Serializable {
transient volatile HashEntry[] table;
transient int count;
}
// 根本节点,存储Key, Value值言情小说,Java同步容器和并发容器,guess是什么牌子
static final class HashEntry {
final int hash;
final K key;
volatile V value;
volatile HashEntry next;
}
}

JDK8

  • jdk8 中首要做了 2 方面的改善
  • 撤销性包厢 segments 字段,直接选用 transient volatile Ha言情小说,Java同步容器和并发容器,guess是什么牌子shEntry[] table 保存数据,选用 table 数组元素作为锁,然后完成了对每一行数据进行加锁,进一步削减并发抵触的概率。
  • 将原先 table 数组+单向链表的数据结构,变更为 table 数组+单向链表+红黑树的结构。关于 hash 表来说,最中心的才能在于将 key hash 之后能均匀的散布在数组中。假如 h摸下体ash 之后散列的很均匀,那么 table 数组中的每个行列长度首要为 0 或许 1。但实际状况并非总是如此抱负,赤道银行是什么意思尽管 ConcurrentHashMap 类默许的加载因子为 0.75,可是在数据量过大或许命运欠安的状况下,仍是会存在一些行列长度过长的状况,假如仍是选用单向列表办法,那么查询某个节点的时刻复杂度为 O(n);因而,关于个数超越 8(默许值)的列表,jdk1.8 中选用了红黑树的结构,那么查询的时刻复杂度能够降低到 O(logN),能够改善功能。
final V p光伏发电utVal(K key, V value, Boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node[] tab = table;;) {
Node f;
int n, i, fh;
// 假如table为空,初始化;不然,依据hash值核算得到数组索引i,假如tab[i]为空,直接新建节点Node即可。注:tab[i]本质为链表或许红黑树的首节点。
if (tab == null || (n = tab.length) == 0)
tab = initTabl广汽三菱e(); else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null, new Node(hash, key, value, null))) break;
// no lock when adding to empty bin
}
generate// 假如tab[i]不为言情小说,Java同步容器和并发容器,guess是什么牌子空而且hash值为MOVED,阐明该链表正在进行transfer操作,回来扩容完成后的table。 else if ((fh = f.hash) == MOVED)
tab = hel言情小说,Java同步容器和并发容器,guess是什么牌子pTransfer(tab, f); else {
V oldVal = null;
// 针对首个节点进行加锁操作,而不是segment,进一步削减线程抵触
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node e = f;; ++binCount) {
K ek;
// 假如在链表中找到值为key的节点e,直接设置e.val = value即可。
if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
oldVal = e.val;
if至少还有你 (!onlyIfAbsent)
e.val = value;
break;对不住
}
// 假如没有找到值为key的节点,直接新建Node并参加链表即可。
Node pred = e;
if ((e = e.next) == null) {
pred.next = new Node(hash, key,
value, null);
break;
}
}
}
// 假如首节点为TreeBin类型,阐明为红黑树结构,履行putTreeVal操作。 else if (f instanceof TreeBin) {
Node p;
binCount = 2;
if ((p = ((TreeBin)f).putTreeVal(hash, key,
value)) != null) {
oldVal dx= p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCou战神榜吴迪nt != 0) {
// 假如节点数>=8,那么转化链表结构为红黑树结构。
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null) return oldVal;
break;
}
}
}
// 计数增加1,有或许触发言情小说,Java同步容器和并发容器,guess是什么牌子transfer操作(扩容)。
addCount(1L, binCount);
return null;
}

示例

public class ConcurrentHashMapDemo { public static void main(String[] args) throws InterruptedException { // HashMap 在并发迭代拜访时会抛出 ConcurrentModificationException 反常 // Map map = new HashMap<>();
Map map = new ConcurrentHashMap<>();
Thread wthread = new Thread(() -> {
System.out.println("写操作线程开端履行"); for (int i = 0; i < 26; i++) {
map.put(i, (char) ('a' + i));
}
});
Thread rthread = new Thread(() -> {
System.out.println("读操作线程开端履行"); for (Integer key : map.keySet()) {
System.out.println(key + " - " + map.get(key));
}
});
wthread.start();
rthread.start();
Thread.sleep(1000);
}
}

CopyOnWriteArrayList

关键

  • 效果:CopyOnWrite 字面意思为写入时仿制。CopyOnWriteArrayList 是线程安全的 ArrayList。
  • 原理:
  • 在 CopyOnWriteAarrayList 中,读操作不同步,由于它们在内部数组的快照上作业,所以多个迭代器能够一起遍历而不会彼此堵塞(1,2,4)。
  • 一切的写操作都是同步的。他们在备份数组(3)的副本上作业。写操作完成后,后备阵列将被替换为仿制的阵列,并开释确定。支撑数组变得易变,所以替换数组的调用是原子(5)。
  • 写操作后创立的迭代器将能够看到修正的结构(6,7)。
  • 写时仿制调集回来的迭代器不会抛出 ConcurrentModificationException,由于它们在数组的快照上作业,而且不管后续的修正(2,4)怎么,都会像迭代器创立时那样彻底回来元素。

源码

重要特点

  • lock - 履行写时仿制操作,需求运用可重入锁加锁
  • array - 目标数组,用于寄存元素
 
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;

重要办法

  • 增加操作
  • 增加的逻辑很简单,先将原容器 copy 一份,然后在新副本上履行写操作,之后再切换引证。当然此进程是要加锁的。
 
public Boolean add(E e) {
//ReentrantLock加锁,确保线程安全
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//复制原容器,长度为原容器长度加一
Object[] newElements = Arrays.copyOf(elements, len + 1);
//在新副本上履行增加操作
newElements[len] = e;
//将原容器引证指向新副本
setArray(newElements);
return true;
}
finally {
//解锁
lock.unlock();
}
}

删去操作

  • 删去操作同理,将除要删去元素之外的其他元素复制到新副本中,然后切换引证,将原容器引证指向新副本。同属写操作,需求加锁。
public E remove(int index) {
//加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0) //假如要删去的是列表结尾数据,复制前len-1个数据到新副本上,再切换引证
setArray(Arrays.copyOf(elements, len - 1)); else {
//不然,将除要删去元素之外的其他元素复制到新副本中,并切换引证
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
}
finally {
//解锁
lock.unlock();
}
}

  • 读操作
  • CopyOnWriteArrayList 的读操作是不必加锁的,功能很高。
public E get有教无类(int index) {
return get(getArray(), index);青年医师
}
private E get(Object[] a, int index) {
return (E) a[index];
}

示例

public class CopyOnWriteArrayListDemo { static class ReadTask implements Runnable {
List list;
ReadTask(List list) {
this.list = list;
}
public void run() {
for (String str : list) {
System.out.println(str);
}
}
}
static class WriteTask implements Runnable {
List list;
int index;
WriteTask(List list, int index) {
this.list = list;
this.index = index;
}
public void run() {
list.remove(index);
list.东方神龙啸异世add(index, "write_" + index);
}
}
public void run() {
final int NUM = 10;
// ArrayLi拔丝红薯的做法st 在并发迭代拜访时会抛出 ConcurrentModificationException 反常 // List list = new ArrayList<>();
CopyOnWriteArrayList list = new CopyOnWriteArrayList<>();
for (int i = 0; i < NUM; i++) {
list.add("main_" + i);
}
ExecutorService executorService = Executors.newFixedThreadPool(NUM);
for (int i = 0; i < NUM; i++) {
executorService.execute(new ReadTask(list));
executorService.execute(new WriteTask(list, i));
}
executorService.shutdown();
}
public static void mphoneain(String[] args) {
new CopyOnWriteArrayListDemo().run();
}
}


喜爱这篇文章的朋友能够点个喜爱,也能够重视一下我

the end
中欧合作进入新时代,深兰科技AI渡海落地意大利