并发编程(三):同步容器和并发容器

前言

Java 中有些集合和非线程安全,而有些集合是线程安全,后者又被称为是Java中的同步容器,因为它能满足操作的原子性,保持数据同步。有些容器时Java自带的,而有些是通过Collections提供的方法包装的。

同步容器

在Java中,同步容器主要包括2类:
1)Vector、Stack、HashTable

Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。

Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。

HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。

2)Collections类中提供的静态工厂方法创建的类

Collections类是一个工具提供类。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。更重要的是,在它里面提供了几个静态工厂方法来创建同步容器类,同步容器中的方法采用了synchronized进行了同步,缺点是必然会影响到程序的执行性能。

同步容器和并发容器的区别

同步容器类虽然都是线程安全的,但是在某些情况下(复合操作),仍然需要加锁来保护;

同步容器会导致多个线程中对容器方法调用的串行执行,降低并发性,因为它们都是以容器自身对象为锁,所以在需要支持并发的环境中,可以考虑使用并发容器来替代。

并发容器:并发容器是针对多个线程并发访问设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,如ConcurrentHashMap,CopyOnWriteArrayList等。并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量。

另外并发容器提供了一些在使用同步容器时需要自己实现的复合操作,包括putIfAbsent等,但是由于并发容器不能通过加锁来独占访问,所以我们无法通过加锁来实现复合操作了。

复合操作

同步容器中的所有自带方法都是线程安全的,因为方法都使用synchronized关键字标注。但是,对这些集合类的复合操作无法保证其线程安全性。对于什么是复合操作一直很不理解?为甚线程安全的同步容器遇到符合操作后会变的线程不安全了?下面以线程安全的Vector做例说明。

像Vector这样的同步容器的所有共有方法全都是synchronized的。也就是说,我们可以在多线程场景中放心的使用单独这些方法,因为这些方法本身的确是线程安全的。那么为什么又说复合操作无法保证线程安全呢?这里举个栗子,我们定义如下删除Vector中最后一个元素方法:

public Object deleteLast(Vector v){
    int lastIndex  = v.size()-1;
    v.remove(lastIndex);
}

上面这个方法是一个复合方法,包括size()和remove(),无论是size()方法还是remove()方法都是线程安全的,但是,如果多线程调用该方法的过程中有,remove方法有可能抛出ArrayIndexOutOfBoundsException。因为在多线程情况下,这块代码是没有锁住的,也就是说有可能出现并发,当A线程执行int lastIndex = v.size()-1;后,其他多个线程也同样执行,并且减少到0时,也就是说,当当前索引值不再有效的时候,将会抛出这个异常。

解决:为了避免复合操作带来的不安全,可以给这个复合操作加上锁来同步代码块;或者使用不同于同步容器的并发容器来解决这个问题;

并发容器

从Java5开始,Java.util.concurent包下,提供了大量支持高效并发的访问的集合类;这些线程分为两类:

1.以Concurrent开头的集合类:如ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentskipListSet、ConcurrentLinkedQueue和ConcurrentLinkedDeque;

以Concurrent开头的集合类支持并发访问,写入线程的所有操作都是锁安全的,但是读取操作时没有锁安全的,在并发写入操作时性能良好;
ConcurrentLinkedQueue:不允许使用Null元素,多个线程访问时无需等待,具有高效访问的特效,适用于多个线程访问一个集合;
ConcurrentHashMap:支持16个线程的并发写入,当超过16时,多出的线程等待,此外可以通过concurrencyLevel参数设定线程数;

2.以CopyOnWrite开头的集合类:如CopyOnWriteArrayList、CopyOnWriteArraySet;

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
嗨!很高兴回答你关于Java并发编程的问题。请问你想知道什么方面的内容呢?我可以分享一些学习笔记和建议给你。 1. 并发编程基础:了解并发编程的基本概念,如线程、进程、锁、同步等。学习Java中的并发编程模型以及相关的API,如Thread、Runnable、Lock、Condition等。 2. 线程安全性:学习如何保证多线程环境下的数据安全性,了解共享资源的问题以及如何使用同步机制来防止数据竞争和并发问题。 3. 线程间的通信:掌握线程间的通信方式,如使用wait/notify机制、Lock/Condition等来实现线程的协调与通信。 4. 并发容器:学习并发容器的使用,如ConcurrentHashMap、ConcurrentLinkedQueue等。了解它们的实现原理以及在多线程环境下的性能特点。 5. 并发工具类:熟悉Java提供的并发工具类,如CountDownLatch、CyclicBarrier、Semaphore等,它们可以帮助你更方便地实现线程间的协作。 6. 并发编程模式:学习一些常见的并发编程模式,如生产者-消费者模式、读者-写者模式、线程池模式等。了解这些模式的应用场景和实现方式。 7. 性能优化与调试:学习如何分析和调试多线程程序的性能问题,了解一些性能优化的技巧和工具,如使用线程池、减少锁竞争、避免死锁等。 这些只是一些基本的学习笔记和建议,Java并发编程是一个庞大而复杂的领域,需要不断的实践和深入学习才能掌握。希望对你有所帮助!如果你有更具体的问题,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值