有时候会遇到某些情况,我们需要一个存储不重复的容器。这时候我们会想到set,那么在多线程并发操作的情况下,我们就需要做同步。
Set怎么出现同步集合呢?
如果要同步非同步的集合

Collection c=Collections.synchronizedCollection(new ArrayList());
List list=Collections.synchronizedList(new ArrayList());
Set s=Collections.synchronizedSet(new HashSet());
Map m=Collections.synchronizedMap(new HashMap());

上网找到这几种方法,可以同步原本非同步的集合。但是这样就能确保没有问题么,我们运行下边的代码:

/**
* Created by STVEN0KING
* Date: 2016/10/31.
* Description:
* 该异常表示迭代器迭代过程中,迭代的对象发生了改变,如数据项增加或删除。
* [解决方案]:由于迭代对象不是线程安全,在迭代的过程中,会检查modCount是否和初始modCount即expectedModCount一致,
* 如果不一致,则认为数据有变化,迭代终止并抛出异常。
* 常出现的场景是,两个线程同时对集合进行操作,线程1对集合进行遍历,
* 而线程2对集合进行增加、删除操作,此时将会发生ConcurrentModificationException异常。
* 解决方法:多线程访问时要增加同步锁,或者建议使用线程安全的集合:
* 如ConcurrentHashMap替换HashMap,CopyOnWriteArrayList替换ArrayList。
*/
public class HashSetTest
public static void main(String[] args) {
Set<Long> s = Collections.synchronizedSet(new HashSet<Long>());

new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
//synchronized (s) {
s.add((long) i);
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
//synchronized (s) {
for (Long l : s) {
System.out.println(l);
}
//}
} catch

结果会抛出异常:

java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(HashMap.java:1437)
at java.util.HashMap$KeyIterator.next(HashMap.java:1461)
at com.tzx.HashSetTest$2.run(HashSetTest.java:35)
at java.lang.Thread.run(Thread.java:745)

为什么呢,我们来看看源码

//Collections.java部分代码省略
public static <T> Set<T> synchronizedSet(Set<T> s) {
return new SynchronizedSet<>(s);
}

static class SynchronizedSet<E>
extends SynchronizedCollection<E>
implements Set<E> {
private static final long serialVersionUID = 487447009682186044L;

SynchronizedSet(Set<E> s) {
super(s);
}
SynchronizedSet(Set<E> s, Object mutex) {
super(s, mutex);
}

public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return c.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return c.hashCode();}
}
}

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;

final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize

SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}

SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}

public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized (mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}
public <T> T[] toArray(T[] a) {
synchronized (mutex) {return c.toArray(a);}
}

public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}

public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
}

public void clear() {
synchronized (mutex) {c.clear();}
}
public String toString() {
synchronized (mutex) {return c.toString();}
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {c.forEach(consumer);}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) {return c.removeIf(filter);}
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
@Override
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!
}
@Override
public Stream<E> parallelStream() {
return c.parallelStream(); // Must be manually synched by user!
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized

我们重点看SynchronizedCollection类的add和iterator方法,add方法自己就做了同步,而iterator(// Must be manually synched by user!)。

所以是做并发读写的时候我们需要自己做同步(PS:打开上面代码中的注释)。