同步容器只是线程安全性更好一些,在代码运行逻辑中并不一定是线程安全的。也就是同步容器本身是线程安全的,但是主要还是注意你的代码逻辑。
一、非线程安全的集合对应的同步容器
Vector实现了List接口,Vector里面的方法都是用sync修饰过的。
Stack也是同步容器,也是使用sync实现同步,继承Vector,里面存储的数据是先进后出。
HashTable实现了Map接口,里面方法用sync修饰。
二、Vector
@Slf4j
public class VectorExample1 {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
private static List<Integer> list = new Vector<>();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
update(count);
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("size:{}", list.size());
}
private static void update(int i) {
list.add(i);
}
}
执行可以看到,得到5000,Vector是线程安全的。
但我们之前强调的代码逻辑导致同步容器线程不安全是这样的:
public class VectorExample2 {
private static Vector<Integer> vector = new Vector<>();
public static void main(String[] args) {
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.size(); i++) {
vector.remove(i);
}
}
};
Thread thread2 = new Thread() {
public void run() {
for (int i = 0; i < vector.size(); i++) {
vector.get(i);
}
}
};
thread1.start();
thread2.start();
}
}
}
比如同一时刻,线程1remove(9),而线程2get(9)就会出现数组越界异常。
这就是同步容器里面的两个同步方法,因为操作顺序的差异,不同的线程里面出现线程不安全问题。
三、Collection.synchronizedXXX
synchronizedList、synchronizedSet、synchronizedMap都会生成集合的同步容器。
@Slf4j
public class SetExample {
// 请求总数
public static int clientTotal = 5000;
// 同时并发执行的线程数
public static int threadTotal = 200;
private static Set<Integer> set = Collections.synchronizedSet(Sets.newHashSet());
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
update(count);
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("size:{}", set.size());
}
private static void update(int i) {
set.add(i);
}
}
可以看到线程安全。