温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

java高并发同步容器指的是什么

发布时间:2021-10-19 15:54:14 来源:亿速云 阅读:135 作者:柒染 栏目:大数据

这篇文章将为大家详细讲解有关java高并发同步容器指的是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

在java中同步容器主要包括两类:

  • ArrayList -> Vector,Stack;     HashMap -> HashTable(key,value不能为null)

  • Collections.synchronizedXXX(List, Set, Map)

 Vector

Vector实现了List接口,实际上就是一个数组。与ArrayList非常类似。但是Vector中的所有方法都是使用synchronized方法修饰的方法,进行了同步的措施。因此在多线程环境下使用ArrayList对象时,如果被多个线程共享使用可以换成同步的Vector,这样的话线程安全型会更好一些(而不是完全线程安全的)。

@Slf4j
@ThreadSafe
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 InterruptedException {
        //线程池
        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 (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("size:{}",list.size()) ;
    }

    public static void update(int i) {
        list.add(i);
    }

}

这样输出的结果就是预期的结果。

为什么说同步容器不是线程安全的?

@NotThreadSafe
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(){
                @Override
                public void run() {
                    for (int i = 0;i < vector.size();i++) {
                        vector.remove(i);
                    }
                }
            };
            Thread thread2 = new Thread(){
                @Override
                public void run() {
                    for (int i = 0;i < vector.size();i++) {
                        vector.get(i);
                    }
                }
            };
            thread1.start();
            thread2.start();
        }

    }
}

运行,抛出异常:

Exception in thread "Thread-611" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 5
	at java.util.Vector.get(Vector.java:748)
	at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25)
Exception in thread "Thread-1759" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 21
	at java.util.Vector.get(Vector.java:748)
	at com.vincent.example.syncContainer.VectorExample2$2.run(VectorExample2.java:25)

原因:get发生越界肯定是remove方法引起的,vector虽然能保证同一时刻只能有一个线程能访问他,但是不排除有这种可能:当某个线程某个时刻执行到int i = 0;i < vector.size()时,vector.size()返回10,i=9;而另外一个线程正好将i=9的vector移除掉了,这时get方法想调用i=9的元素就会出现数组越界的异常。

这个例子演示了两个同步容器的两个同步方法因为操作顺序的差异,在不同线程里面可能会触发线程不安全的问题。因此为了保证线程安全,必须在方法调用端做一些额外的同步措施才可以。在使用同步容器时并不是在所有场合下都是线程安全的。

Stack

Stack中的方法也使用了synchronized修饰了,实际上Stack类继承了Vector类。

HashTable

HashTable实现了Map接口,与HashMap很相似,但是HashTable进行了同步处理,方法也是使用了synchronized进行了修饰。但是在使用HashTable时一定要注意key和value是不能为null的。

Collections

将新建ArrayList、HashSet、HashMap对象由Collections产生:

private static List<Integer> list = Collections.synchronizedList(new ArrayList<>());
private static Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
private static Map<Integer, Integer> map = Collections.synchronizedMap(new HashMap<>());

在集合遍历过程中删除操作

public class VectorExample3 {

    //Exception in thread "main" java.util.ConcurrentModificationException
    private static void test1(Vector<Integer> v1){ //foreach
        for(Integer i: v1){
            if(i.equals(3)){
                v1.remove(i);
            }
        }
    }
    //Exception in thread "main" java.util.ConcurrentModificationException
    private static void test2(Vector<Integer> v1){ //iterator
        Iterator<Integer> integerIterator = v1.iterator();
        while (integerIterator.hasNext()) {
            Integer i = integerIterator.next();
            if(i.equals(3)){
                v1.remove(i);
            }
        }
    }
    // success
    private static void test3(Vector<Integer> v1){
        for(int i = 0; i < v1.size(); i++) {
            if(v1.equals(3)){
                v1.remove(i);
            }
        }
    }

    public static void main(String[] args) {
        Vector<Integer> vector = new Vector<>();
        vector.add(1);
        vector.add(2);
        vector.add(3);
        test1(vector);
    }
}

如果使用了Foreach或者迭代器来循环我们的集合时,尽量不要在循环中做集合的删除操作,如果要做remove操作时,建议在遍历的过程中发现需要删除的值然后做一个标记,在遍历结束后在执行相应的remove操作。在多线程情况下出现异常的情况会更大。

关于java高并发同步容器指的是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI