Java多线程之线程安全策略---同步容器

同步容器只是线程安全性更好一些,在代码运行逻辑中并不一定是线程安全的。也就是同步容器本身是线程安全的,但是主要还是注意你的代码逻辑。

一、非线程安全的集合对应的同步容器


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

synchronizedListsynchronizedSetsynchronizedMap都会生成集合的同步容器。

@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);
    }
}
可以看到线程安全。




猜你喜欢

转载自blog.csdn.net/weixin_40459875/article/details/80294750