首页 > 分享 > Java线程安全之CopyOnWriteArraySet 应用详解

Java线程安全之CopyOnWriteArraySet 应用详解

                             Java线程安全

                      —CopyOnWriteArraySet应用详解

                                                                                             龚建鹏  150342208

概述:

             

             CopyOnWriteArraySet相当于线程安全的HashSet,它是一个线程安全的无序、不可重复集合。CopyOnWriteArraySet和HashSet都继承共同的父类AbstractSet。

            在CopyOnWriteArraySet的Javadoc中有这么一段话:

   A java.util.Set that uses an internal CopyOnWriteArrayList for all of its operations. Thus, it shares the same basic properties:
•It is best suited for applications in which set sizes generally stay small, read-only operations vastly outnumber mutative operations, and you need to prevent interference among threads during traversal.
•It is thread-safe.
•Mutative operations (add, set, remove, etc.) are expensive since they usually entail copying the entire underlying array.
•Iterators do not support the mutative remove operation.
•Traversal via iterators is fast and cannot encounter interference from other threads. Iterators rely on unchanging snapshots of the array at the time the iterators were constructed.

  翻译过来就是:

    对所有操作使用内部CopyOnWriteArrayList的java.util.Set。 因此,它具有相同的基本属性:

•它最适合应用程序的集合大小通常很小,只读操作远远超过可变操作,并且需要防止在遍历期间线程之间的干扰。
•线程安全。
•突变操作(添加,设置,删除等)是昂贵的,因为它们通常需要复制整个底层阵列。
迭代器不支持可变删除操作。
•迭代器遍历速度快,不会受到来自其他线程的干扰。 迭代器构建时迭代器依赖于数组的不变快照。

CopyOnWriteArraySet的UML图:         
                                           


       说明:

    1、CopyOnWriteArraySet继承于AbstractSet,这就意味着它是一个集合。

    2、因为CopyOnWriteArraySet是所有操作都使用内部CopyOnWriteArrayList的Set集合,所以CopyOnWriteArraySet相当于动态数组实现的“集合”,它里面的元素不能重复。

    3、CopyOnWriteArraySet的“线程安全”机制是通过volatile和互斥锁来实现的。

CopyOnWriteArraySet的方法摘要:

// 创建一个空 set。

CopyOnWriteArraySet()

// 创建一个包含指定 collection 所有元素的 set。

CopyOnWriteArraySet(Collection<? extends E> c)

//将指定元素添加到此列表的尾部。

boolean add(E e)

//按照指定 collection 的迭代器返回元素的顺序,将指定 collection 中的所有元素添加此列表的尾部。

boolean addAll(Collection<? extends E> c)

//从此列表移除所有元素。

void clear()

//如果此列表包含指定的元素,则返回 true。

boolean contains(Object o)

//如果此列表包含指定 collection 的所有元素,则返回 true。

boolean containsAll(Collection<?> c)

//比较指定对象与此列表的相等性。

boolean equals(Object o)

//对Iterable的每个元素执行给定的操作,直到处理完所有元素或操作抛出异常。

void forEach(Consumer<? super E> action)

//如果此 set 不包含任何元素,则返回 true。

boolean isEmpty()

//返回按照元素添加顺序在此 set 中包含的元素上进行迭代的迭代器。

Iterator<E> iterator()

//如果指定元素存在于此 set 中,则将其移除。

boolean remove(Object o)

//移除此 set 中包含在指定 collection 中的所有元素。

boolean removeAll(Collection<?> c)

//删除满足条件的所有这个集合的元素

boolean removeIf(Predicate<? super E> filter)

//仅保留此 set 中那些包含在指定 collection 中的元素。

boolean retainAll(Collection<?> c)

//返回此 set 中的元素数目。

int size()

//按照添加这些元素的顺序,返回此集合中的元素的Spliterator。

Spliterator<E> spliterator()

//返回一个包含此 set 所有元素的数组。

Object[] toArray()

// 返回一个包含此 set 所有元素的数组;返回数组的运行时类型是指定数组的类型。

<T> T[] toArray(T[] a)

CopyOnWriteArraySet源代码分析:

    (1)从CopyOnWriterArraySet的成员变量可以看出它包含CopyOnWriterArrayList对象,它是通过CopyOnWriterArrayList实现的。而CopyOnWriterArrayList本质是一个动态数组队列,所以正如前面说过的CopyOnWriterArraySet相当于通过动态数组实现的“集合”。只是CopyOnWriterArrayList中允许有重复的元素;但CopyOnWriterArraySet中元素不能重复。

 

   (2)对CopyOnWriterArraySet的主要方法进行分析:

public CopyOnWriteArraySet() {

al = new CopyOnWriteArrayList<E>();

}

public CopyOnWriteArraySet(Collection<? extends E> c) {

if (c.getClass() == CopyOnWriteArraySet.class) {

@SuppressWarnings("unchecked")

CopyOnWriteArraySet<E> cc = (CopyOnWriteArraySet<E>) c;

al = new CopyOnWriteArrayList<E>(cc.al);

} else {

al = new CopyOnWriteArrayList<E>();

al.addAllAbsent(c);

}

}

public Spliterator<E> spliterator() {

return Spliterators.spliterator(al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);

}

@SuppressWarnings("unchecked")

public <T> T[] toArray(T a[]) {

Object[] elements = getArray();

int len = elements.length;

if (a.length < len)

return (T[]) Arrays.copyOf(elements, len, a.getClass());

else {

System.arraycopy(elements, 0, a, 0, len);

if (a.length > len)

a[len] = null;return a;

}

}

public void clear() {

final ReentrantLock lock = this.lock;

try {

setArray(new Object[0]);

}

finally {

lock.unlock();

}

}

public void forEach(Consumer<? super E> action) {

if (action == null) throw new NullPointerException();

Object[] elements = getArray();

int len = elements.length;

for (int i = 0; i < len; ++i) {

@SuppressWarnings("unchecked")

E e = (E) elements[i];

action.accept(e);

}

}

public boolean removeIf(Predicate<? super E> filter) {

if (filter == null) throw new NullPointerException();

final ReentrantLock lock = this.lock;

lock.lock();

try {

Object[] elements = getArray();

int len = elements.length;

if (len != 0) {

int newlen = 0;

Object[] temp = new Object[len];

for (int i = 0; i < len; ++i) {

@SuppressWarnings("unchecked")

E e = (E) elements[i];

if (!filter.test(e))

temp[newlen++] = e;

}

if (newlen != len) {

setArray(Arrays.copyOf(temp, newlen));

return true;

}

}

return false;

} finally {

lock.unlock();

}

}

    在这里我只介绍一些新增加的和比较重要的方法,其他的一些方法都比较简单,你们可以通过API手册自己理解,这里我就不再累赘了,如果有兴趣可以自己尝试的去做。

CopyOnWriteArraySet的示例:

package com.gong.copyonwritearrayset;

import java.util.Iterator;

import java.util.Set;

import java.util.concurrent.CopyOnWriteArraySet;

public class CopyOnWriteArraySetTest {

private static CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<String>();

public static void main(String[] args) throws InterruptedException {

CopyOnWriteArraySetTest test = new CopyOnWriteArraySetTest();

test.new WriteThread("ta").start();

test.new WriteThread("tb").start();

}

private static void printAll() {

String value = null;

Iterator<String> iter = set.iterator();

while(iter.hasNext()) {

value = (String)iter.next();

System.out.print(value + ", ");

}

System.out.println();

}

class WriteThread extends Thread {

WriteThread(String name) {

super(name);

}

@Override

public void run() {

int i = 0;

while (i++ < 5) {

String val = Thread.currentThread().getName() + "-" + i;

set.add(val);

printAll();

}

}

}

}

结果:

只运行一个进程:

<span style="font-size:14px;"> ta-1, ta-1, ta-2, ta-1, ta-2, ta-3, ta-1, ta-2, ta-3, ta-4, ta-1, ta-2, ta-3, ta-4, ta-5, </span>

两个进程一起运行:

<span style="font-size:14px;"> ta-1, ta-1, tb-1, tb-1, ta-1, ta-1, tb-1, tb-1, ta-2, ta-2, ta-1, tb-2, tb-1, ta-1, ta-2, tb-1, tb-2, ta-2, ta-3, tb-2, ta-1, ta-3, tb-1, tb-3, ta-2, ta-1, tb-2, tb-1, ta-3, ta-2, tb-3, tb-2, ta-4, ta-3, ta-1, tb-3, tb-1, ta-2, tb-2, ta-3, tb-3, ta-4, tb-4, ta-5, ta-4, tb-4, ta-1, tb-1, ta-2, tb-2, ta-3, tb-3, ta-4, tb-4, ta-5, tb-5, </span>

             当只有一个进程运行时我们可以看到set集合中没有重复元素,因为该进程开始运行时,每存入一个元素就会将该集合中的所有元素输出,不存在两个线程的同步问题。但当我启动两个进程时,就会出现上面的情况,这是因为每个进程都是独立的,没有哪个进程的优先级更高,所以当谁获得了CPU的资源谁就会执行它的操作,因此才会出现上面这种情况,但每个set集合中的元素并不是重复的,这个一定要牢记。我又尝试了将一个进程暂时休眠,等一个进程运行完毕之后它再运行,这样就可以更清楚的观察到他们的执行过程。从图中也可以看到Set集合中的确没有重复的元素。

         

相关知识

Java十大经典案例源码解析与实战应用
Java内存区域
借花献佛!朋友干了5年整的Java面试官,给我分享了一份面试官最爱问的Java面试题
移动应用入门级学习认证
移动(应用)开发详解
Python中的线程池和进程池的详解
使用java编写一只玫瑰花
移动应用安全开发要求(1,源码安全)
网上鲜花订购系统(鲜花管理、用户留言版)
编程实践精华总结集锦系列1: SpringBoot/Maven/IDEA/Java/Kotlin/Redis等等

网址: Java线程安全之CopyOnWriteArraySet 应用详解 https://m.huajiangbk.com/newsview1109525.html

所属分类:花卉
上一篇: 服务器一直显示session o
下一篇: 2020年掘安杯网络安全技能挑战