精华内容
下载资源
问答
  • Java哪些线程安全容器

    千次阅读 2018-12-12 17:53:28
    同步容器类:使用了synchronized 1.Vector 2.HashTable 并发容器: 3.ConcurrentHashMap:分段 ...6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。 (java....

    同步容器类:使用了synchronized
    1.Vector
    2.HashTable

    并发容器:
    3.ConcurrentHashMap:分段
    4.CopyOnWriteArrayList:写时复制
    5.CopyOnWriteArraySet:写时复制

    Queue:
    6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。
    (java.util.concurrent.BlockingQueue 接口代表了线程安全的队列。)
    7.ArrayBlockingQueue:基于数组的有界阻塞队列
    8.LinkedBlockingQueue:基于链表的有界阻塞队列。
    9.PriorityBlockingQueue:支持优先级的无界阻塞队列,即该阻塞队列中的元素可自动排序。默认情况下,元素采取自然升序排列
    10.DelayQueue:一种延时获取元素的无界阻塞队列。
    11.SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待一个take操作,否则不能继续添加元素。内部其实没有任何一个元素,容量是0

    Deque:
    (Deque接口定义了双向队列。双向队列允许在队列头和尾部进行入队出队操作。)
    12.ArrayDeque:基于数组的双向非阻塞队列。
    13.LinkedBlockingDeque:基于链表的双向阻塞队列。

    Sorted容器:
    14.ConcurrentSkipListMap:是TreeMap的线程安全版本
    15.ConcurrentSkipListSet:是TreeSet的线程安全版本

    原文:https://blog.csdn.net/u010002184/article/details/74892663

    展开全文
  • java线程安全容器哪些

    万次阅读 2017-07-09 16:35:27
    同步容器类:使用了synchronized Vector HashTable 并发容器: ConcurrentHashMap:分段 ...ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。
    同步容器类:使用了synchronized
    
    1.Vector
    2.HashTable

    并发容器:
    3.ConcurrentHashMap:分段
    4.CopyOnWriteArrayList:写时复制
    5.CopyOnWriteArraySet:写时复制

    Queue:
    6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。
    (java.util.concurrent.BlockingQueue 接口代表了线程安全的队列。)
    7.ArrayBlockingQueue:基于数组的有界阻塞队列
    8.LinkedBlockingQueue:基于链表的有界阻塞队列。
    9.PriorityBlockingQueue:支持优先级的无界阻塞队列,即该阻塞队列中的元素可自动排序。默认情况下,元素采取自然升序排列
    10.DelayQueue:一种延时获取元素的无界阻塞队列。
    11.SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待一个take操作,否则不能继续添加元素。内部其实没有任何一个元素,容量是0

    Deque:
    (Deque接口定义了双向队列。双向队列允许在队列头和尾部进行入队出队操作。)
    12.ArrayDeque:基于数组的双向非阻塞队列。
    13.LinkedBlockingDeque:基于链表的双向阻塞队列。

    Sorted容器:
    14.ConcurrentSkipListMap:是TreeMap的线程安全版本
    15.ConcurrentSkipListSet:是TreeSet的线程安全版本

    参考:
    http://blog.csdn.net/zhangchaoyangsun/article/details/8664526
    http://www.cnblogs.com/shijiaqi1066/p/3412275.html
    http://techlog.cn/article/list/10183041
    展开全文
  • Java线程安全容器与介绍

    千次阅读 2017-09-27 23:18:55
    Java基本容器介绍 Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。Set和List两个类继承于它。Set中不能包含重复的元素,也没有顺序来存放。而List是一个有序的集合,可以包含重复的元素。...

    Java基本容器介绍

    Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。Set和List两个类继承于它。Set中不能包含重复的元素,也没有顺序来存放。而List是一个有序的集合,可以包含重复的元素。
    而Map又是另一个接口,它和Collection接口没有关系。Map包含了key-value键值对,同一个Map里key是不能重复的,而不同key的value是可以相同的。

    这里写图片描述
    Java集合容器框架图:
    这里写图片描述

    1.1. List

    List在collection中的框架图:
    这里写图片描述

    1.1.1. ArrayList

    1) ArrayList实现了可变大小的数组。它允许所有元素,包括null元素。ArrayList没有同步。
    2)size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。Size是返回元素的个数。
    3) 每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
    4)和LinkedList一样,ArrayList也是非同步的(unsynchronized)。
    5)由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历ArrayList,而不是用来插入和移除元素。因为那比LinkedList开销要大很多。

    在存放数据的数组长度不够时,会进行扩容,即增加数组长度。在Java 8中是默认扩展为原来的1.5倍。

    1.1.2. linkedList

    LinkedList是双向链接串列(doubly LinkedList),也允许null元素。
    当数据量较小时,测试程序中,大约小于30的时候,两者效率差不多,没有显著区别;当数据量较大时,大约在容量的1/10处开始,LinkedList的效率就开始没有ArrayList效率高了,特别到一半以及后半的位置插入时,LinkedList效率明显要低于ArrayList,而且数据量越大,越明显。
    当使用list时,一般情况下首选ArrayList,由于LinkedList可以实现栈、队列以及双端队列等数据结构,所以当特定需要时候,使用LinkedList。

    1.2. Set

    Set接口继承了Collection接口,具有和collection一样的接口,没有任何额外的功能。同时set中的元素是无序的,存储顺序和取出顺序不一致,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
    Set集合中不能包含重复的元素,每个元素必须是唯一的。你只需将元素加入set中,重复的元素会自动移除。Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个Object。Set的特征也继承了那些内部Map实现的特征。
    Set有三种常见的实现:HashSet、 TreeSet和LinkedHashSet。

    1.2.1. HashSet

    HashSet不允许重复(HashMap的key不允许重复,如果出现重复就覆盖),允许null值,非线程安全。 当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等
    注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其 hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

    1.2.2. LinkedHashSet

    LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺 序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
    LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
    LinkedHashSet就是基于LinkedHashMap实现的。LinkedHashSet介于HashSet和TreeSet之间。它也是一个hash表,但是同时维护了一个双链表来记录插入的顺序。

    1.2.3. TreeSet

    TreeSet类型是J2SE中唯一可实现自动排序的类型
    TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向 TreeSet中加入的应该是同一个类的对象。
    TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
    TreeSet采用红黑树算法实现,不允许重复,不允许null值,默认按照升序排序。

    1.3. Map

    Map:一组成对的“键值对”对象,允许你使用键来查找值。其实Map相当于ArrayList或者更简单的数组的一种扩展、推广。在数组中我们可以利用下标即数字访问数组当中的不同元素,那么数字与对象之间形成了一种关联,那么如果将这个数字的概念扩展成为对象,那同样的我们可以将对象与对象之间关联起来。即Map,也称为映射表、关联数组、字典允许我们使用一个对象来查找某个对象。
    Map容器框架图如图所示:

    这里写图片描述

    Map抽象出来AbstractMap用来继承,该类实现了Map中的大部分API,其他Map的具体实现就可以通过直接继承AbatractMap类即可。
    SortedMap也是一个接口,它继承与Map接口。SortedMap中的内容与Map中的区别在于,它是有序的键值对,里面排序的方法是通过比较器(Comparator)实现的。
    基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

    1.3.1. HashTable

    HashTable不接受null键和值,是线程安全的。HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;
    由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。如果使用Java 5以上,请直接使用concurrentHashMap。

    1.3.2. HashMap

    HashMap其实是一个”链表散列”的数据结构,即数组和链表的结合体。HashMap可以接受null键值和值。

    这里写图片描述

    从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

    1.3.2.1. HashMap的工作原理

    HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来储存Entry对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

    1.3.2.2. 当两个对象的hashcode相同会发生什么

    因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。

    1.3.2.3. 如果两个键的hashcode相同,你如何获取值对象?

    当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象。如果有两个值对象储存在同一个bucket,将会遍历链表直到找到值对象。
    你并没有值对象去比较,你是如何确定确定找到值对象的?
    因为HashMap在链表中存储的是键值对.

    1.3.2.4. 如果HashMap的大小超过了负载因子定义的容量,怎么办?

    默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。
    重新调整HashMap的大小可能存在条件竞争。因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。这个时候,你可以质问面试官,为什么要在多线程的环境下使用HashMap呢?:)

    1.3.3. TreeMap

    TreeMap 是一个有序的key-value集合,它是通过红黑树实现的,也可以实现null键和值。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。

    1.3.3.1. HashMap和TreeMap比较

    (1)HashMap:适用于在Map中插入、删除和定位元素。
    (2)Treemap:适用于按自然顺序或自定义顺序遍历键(key)。
    (3)HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap.
    (4)HashMap 非线程安全 TreeMap 非线程安全
    (5)HashMap的结果是没有排序的,而TreeMap输出的结果是排好序的。

    1.3.4. ConcurrentMap

    ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。ConcurrentHashmap大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响。

    如果我们要统计整个ConcurrentHashMap里元素的大小,就必须统计所有Segment里元素的大小后求和。Segment里的全局变量count是一个volatile变量,那么在多线程场景下,我们是不是直接把所有Segment的count相加就可以得到整个ConcurrentHashMap大小了呢?不是的,虽然相加时可以获取每个Segment的count的最新值,但是拿到之后可能累加前使用的count发生了变化,那么统计结果就不准了。所以最安全的做法,是在统计size的时候把所有Segment的put,remove和clean方法全部锁住,但是这种做法显然非常低效。

    因为在累加count操作过程中,之前累加过的count发生变化的几率非常小,所以ConcurrentHashMap的做法是先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小。

    那么ConcurrentHashMap是如何判断在统计的时候容器是否发生了变化呢?使用modCount变量,在put , remove和clean方法里操作元素前都会将变量modCount进行加1,那么在统计size前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。

    1.3.4.1 ConcurrentHashMap和Hashtable的区别

    Hashtable和ConcurrentHashMap有什么分别呢?它们都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割(segmentation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。
    但ConcurrentHashMap并不能完全替代HashTable。两者的迭代器的一致性不同的,HashTable的迭代器是强一致性的,而ConcurrentHashMap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。

    2. Java同步容器与并发容器

    在Java并发编程中,经常听到同步容器、并发容器之说,那什么是同步容器与并发容器呢?
    同步容器可以简单地理解为通过synchronized来实现同步的容器,比如Vector、Hashtable以及SynchronizedList等容器,如果有多个线程调用同步容器的方法,它们将会串行执行。
    其实同步容器与并发容器都为多线程并发访问提供了合适的线程安全,不过并发容器的可扩展性更高。在Java5之前,程序员们只有同步容器,且在多线程并发访问的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发容器,并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如,在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量,另外,并发容器提供了一些在使用同步容器时需要自己实现的复合操作,包括putIfAbsent等,但是由于并发容器不能通过加锁来独占访问,所以我们无法通过加锁来实现其他复合操作了。

    2.1. 同步容器

    Java常用的容器有ArrayList、LinkedList、HashMap等等,这些容器都是非线程安全的。如果有多个线程并发地访问这些容器时,就会出现问题。
    因此在编写程序时,必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。所以,Java提供了同步容器供用户使用。
    在Java中,同步容器主要包括2类:
    1) Vector、Stack、HashTable
    Vector(synchronized同步的ArrayList)和Stack(继承自Vector,先进后出)、HashTable(继承自Dictionary,实现了Map接口)是比较老的容器,Thinking in Java中明确指出,这些容器现在仍然存在于JDK中是为了向以前老版本的程序兼容,在新的程序中不应该在使用。
    2) Collections类中提供的静态工厂方法创建的类
    Collections类是一个工具提供类。在Collections类中提供了大量的方法,比如对集合或者容器进行排序、查找等操作。更重要的是,在它里面提供了几个静态工厂方法来创建同步容器类。
    例如:
     Collectinons.synchronizedList()
     Collections.synchronizedSet()
     Collections.synchronizedMap()
     Collections.synchronizedSortedSet()
     Collections.synchronizedSortedMap()
    LinkedList、ArrayList等都是非同步的
    例子:
    List list = Collections.synchronizedList(new LinkedList(…));
    从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,那么必然会影响到程序的执行性能。同时复合操作并不是线程安全的。于是Java提供了性能更优并发容器。

    2.2. 并发容器

    java.util.concurrent提供了多种并发容器,总体上来说有4类:
    - 队列Queue类型的BlockingQueue和ConcurrentLinkedQueue
    - Map类型的ConcurrentMap,对应的非并发容器是HashMap
    - Set类型的ConcurrentSkipListSet和CopyOnWriteArraySet
    - List类型的CopyOnWriteArrayList

    3. 要注意的一些重要术语:

    1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。
    2) 快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。
    Fail-fast和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。
    3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

    4. 参考网站

    展开全文
  • Java容器线程安全

    千次阅读 2013-03-13 11:43:45
    同步容器类实现线程安全的方式是:将状态封闭起来,并对每个公有方法进行同步,使得每次只有一个线程能访问容器状态。这里解释一下所谓“状态”指的就是成员变量,“封装起来”即将它们设不private,但是通过公有的...

    同步容器类

    同步容器类包括Vector和Hashtable(二者是早期JDK的一部分),还包括JDK1.2中添加的一些相似的类。同步容器类实现线程安全的方式是:将状态封闭起来,并对每个公有方法进行同步,使得每次只有一个线程能访问容器状态。这里解释一下所谓“状态”指的就是成员变量,“封装起来”即将它们设不private,但是通过公有的方法外界仍然可以访问修改类的私有成员,所以要用synchronized将公有方法进行同步,使得每次只有一个线程能访问容器状态。在多线程环境下调用同步容器类自带的所有方法时,实际上都是在串行执行,所以这严重降低并发性和吞吐量。

    像List、Set、Map这些原本不是同步容器类,也可以通过Collections.synchronizedXXX工厂方法将其变为同步容器类,即对其公有方法进行同步。

    List<Student> students=Collections.synchronizedList(new ArrayList<Student>());

    同步容器类的问题

    容器上常见的操作包括:迭代访问、跳转(根据指定顺序找到当前元素的下一个元素)、条件运算(先检查再操作Check-And-Act,比如若没有则添加)。

    public static Object getLast(Vector vec){
        int lastIndex=vec.size()-1;
        return vec.get(lastIndex);
    }
    public static Object deleteLast(Vector vec){
        int lastIndex=vec.size()-1;
        return vec.remove(lastIndex);
    }
    大线程的环境下,getLast()函数中第一行代码之后第二行代码之前如果执行了deleteLast(),那么 getLast()继续执行第二行就会抛出ArrayIndexOutOfBoundException。所以要把getLast()和deleteLast()都变成原子操作:

    public static Object getLast(Vector vec){
        synchronized(vec){
            int lastIndex=vec.size()-1;
            return vec.get(lastIndex);
        }
    }
    public static Object deleteLast(Vector vec){
        synchronized(vec){
            int lastIndex=vec.size()-1;
            return vec.remove(lastIndex);
        }
    }
    又比如容器上的迭代操作:

    for(int i=0;i<vec.size();i++)
        doSomething(vec.get(i));
    在size()之后get()之前,其他线程可能删除了vec中的元素,同样会导致抛出 ArrayIndexOutOfBoundException。但这并不意味着Vector不是线程安全的,Vector的状态仍然是有效的,而抛出的异常也与其规范保持一致。

    正确的做法是在迭代之前对vector加锁:

    synchronized(vec){
        for(int i=0;i<vec.size();i++)
            doSomething(vec.get(i));
    }

    迭代器与ConcurrentModificationException

    使用for或for-each循环对容器进行迭代时,javac内部都会转换成使用Iterator。在对同步容器类进行迭代时如果发现元素个数发生变化,那么hasNext和next将抛出ConcurrentModificationException这被称为及时失效(fail-fast)。

    List<Student> students=Collections.synchronizedList(new ArrayList<Student>());
    //可能抛出ConcurrentModificationException
    for(Student student:students)
        doSomething(student);
    为了防止抛出ConcurrentModificationException,需要在迭代之前对容器进行加锁,但是如果doSomething()比较耗时,那么其他线程都在等待锁,会极大降低吞吐率和CPU的利用率。不加锁的解决办法是“克隆”容器,在副本上进行迭代,由于副本被封闭在线程内,其他线程不会在迭代期间对其进行修改。克隆的过程也需要对容器加锁,开发人员要做出权衡,因为克隆容器本身也有显著的性能开销。

    隐藏的迭代器

    注意,下面的情况会间接地进行迭代操作,也会抛出ConcurrentModificationException

    1. 容器的toString()、hashCode()和equals()方法
    2. containsAll()、removeAll()、retainAll()等方法
    3. 调用以上方法的方法,比如StringBuildre.append(Object)会调用toString()
    4. 容器作为另一个容器的元素或者键
    5. 把容器作为参数的构造函数
    扯句不相关的话,编译器会把字符串的连接操作转换为调用StringBuildre.append(Object)。《Effective Java》也提出当使用多个"+"进行字符串连接时考虑使用StringBuildre.append()代替,因为每次“+”操作都要完全地拷贝2个字符串,而StringBuilder.append()是在第一个字符串后面连接第2个字符串,跟C++中的Vector是一个原理。

    并发容器

    同步容器将对状态的访问都串行化,以实现他们的线程安全性。这样做的代价是严重降低了并发性和吞吐量。
    并发容器类提供的迭代器不会抛出ConcurrentModificationException,因此不需要在迭代时对容器进行加锁。
    下面介绍Java5.0中新增加的几个并发容器类。

    并发Map

    同步容器类在执行每个操作期间都加了一个锁,在一些操作中例如HashMap.get()可能包含大量的工作,如何hashCode不能均匀地分布散列值,那就需要在很多元素上调用equals,而equals本身就有一定的计算量。 HashMap将每个方法都在同一个锁上同步使得每次只能有一个线程访问容器,ConcurrentHashMap与HashMap不同,它采用了粒度更细的分段锁(Lock Striping)。 结果是任意的读线程可以并发地访问Map,读线程和写线程可以并发地访问Map,一定数量的写线程可以并发地访问Map。而且在单线程的环境下,ConcurrentHashMap比HashMap性能损失很小。
    对于需要在整个Map进行的计算,例如size和isEmpty,ConcurrentHashMap会返回一个近似值而非精确值,因为size()返回的值在计算时可能已经过期了。
    ConcurrentHashMap接口中增加了对一些常见复合操作的支持,例如“若没有则添加”、“若相等则替换”、“若相等则移除”等等,在ConcurrentMap接口中已经声明为原子操作。

    Copy-On-Write容器

    CopyOnWriteArrayList用于替代同步List,同理CopyOnWriteAeeaySet用于替代同步Set,这里就以 CopyOnWriteArrayList为例。 “写时复制(Copy-On-Write)”容器的线程安全性在于:只要发布一个事实不可变的对象,那么在访问对象时就不需要再进一步同步。当需要修改对象的时候都会重新复制发布一个新的容器副本。
    如果一个对象在被创建之后其状态就不能被修改,那这个对象就是不可变对象。不可变对象一定是线程安全的。但要注意即使将所有域声明为final,这个对象仍然有可能是可变的,因为final域中可以保存可变对象的引用。下面举个例子,在可变对象上构建不可变类:
    public final class ThreeStorage{
        private final Set<String> storages=new HashSet<String>();
        public ThreeStorage(){
            storages.add("One");
            storages.add("Two");
            storages.add("Three");
        }
        public boolean isStorage(String name){
            return storages.contains(name);
        }
    }
    虽然Set对象是可变的,但从ThreeStorage的设计上来看,Set对象在构造完成后无法对其进行修改。
    每当需要修改容器时都是复制底层数组,看一下set()函数的的源代码:
    public class CopyOnWriteArrayList<E>{
        private volatile transient Object[] array;
        final Object[] getArray() {
            return array;
        }
        final void setArray(Object[] a) {
            array = a;
        }
        public E set(int index, E element) {
            //获取重入锁
    	final ReentrantLock lock = this.lock;
    	lock.lock();
    	try {
    		Object[] elements = getArray();
    		Object oldValue = elements[index];
                    //使用的是==而非equals
    		if (oldValue != element) {
    			int len = elements.length;
                            //复制底层数组
    			Object[] newElements = Arrays.copyOf(elements, len);
    			newElements[index] = element;
                            //把底层数组写回
    			setArray(newElements);
    		} else {
    			setArray(elements);
    		}
    		return (E)oldValue;
    	} finally {
                    //释放锁
    		lock.unlock();
    	}
        }
    }


    一进入set()函数就加了锁,函数结束时才释放锁。
    因为复制底层数组本身就有一定的开销,所以仅当迭代操作远远多于修改操作时,才应该使用CopyOnWrite容器。

    并发Queue

    基本的Collection就是List、Set和Map,Queue底层是由LinkedList来实现的,因为它去除了List的随机访问功能,因此更高效。
    ConcurrentinkedQueue是一个传统的先进先出队列。PriorityQueue是一个非并发的优先队列(说这个似乎与本文的主题无关)。
    Queue上的操作是阻塞的,如果队列为空,那么获取元素的操作会立即返回空值。BlockingQueue的插入和获取操作是阻塞式的,如果队列为空,那么获取操作将一直阻塞队列中有一个可用的元素;如果队列已满,那么插入操作将一直阻塞。

    Sorted容器

    ConcurrentSkipListMap用于替代SortedMap,ConcurrentSkipListSet用于替代SortedSet。TreeMap和TreeSet分别实现了SortedMap和SortedSet。By the way,既然是"sorted",那么这类容器的元素就必须实现Comparable接口。
    展开全文
  • java容器线程安全

    千次阅读 2013-10-11 13:38:55
    同步容器类实现线程安全的方式是:将状态封闭起来,并对每个公有方法进行同步,使得每次只有一个线程能访问容器状态。这里解释一下所谓“状态”指的就是成员变量,“封装起来”即将它们设不private,但是通过公有的...
  • 哪些集合类是线程安全的?

    千次阅读 2019-08-05 18:06:51
    答案: vector:就比arraylist...这道题目考察的是对Java容器的理解。这几个线程安全的集合类基本上都是jdk1.1中出现的,基本上实现方式就是直接对方法上锁,锁的粒度太大了,所以性能不是很好。 像vector因为效率...
  • Java哪些集合是线程安全

    千次阅读 2018-12-23 11:28:39
    Java哪些集合是线程安全的   java.util.Collection是一个集合接口,Collection接口在java中的具体实现有很多, 其中ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap等都是线程安全的。Vector...
  • 线程安全是保证执行业务逻辑正确的基本前提,为此在多线程开发中,我们尽量采用能保证线程安全的数据结构。 JDK已经为大家准备好了一批好用的线程安全容器类,可以大大减少开发工作量,例如HashTable,ConcurrentHas...
  • Java中绝大部分的集合像什么ArrayList、HashMap等绝大部分不是线程安全的。仅有的线程安全的实现,像HashTable、Vector等在性能上又不好。但是不要怕啊。我们大Java还有并发包(Java.util.concurrent)啊,为高度...
  • Java容器哪些哪些是同步容器,哪些是并发容器?一、基本概念新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格...
  • Java的List如何实现线程安全

    万次阅读 多人点赞 2019-10-14 16:14:33
    Java的List是我们平时很常用的集合,线程安全对于高并发的场景也十分的重要,那么List如何才能实现线程安全呢 ? 加锁 首先大家会想到加锁,例如下面的代码 public class Synchronized{ private List<String>...
  • Java并发编程:线程安全和ThreadLocal

    千次阅读 2018-10-19 00:39:09
    线程安全的概念:当多个线程访问某一个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。 线程安全 说的可能比较抽象,下面就以一个简单的例子来看看什么是线程安全...
  • 线程安全队列Queue

    2018-01-17 19:39:14
    在项目启动时,开一个单线程来专门处理巡检任务的下发给巡检服务组件。使用BlockingQueue阻塞算法。BlockingQueue作为线程容器,可以为线程同步提供有力的保障。
  • Java线程总结之线程安全队列Queue

    万次阅读 2017-09-19 10:21:54
    Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。...
  • java 容器都有哪些

    万次阅读 多人点赞 2019-07-27 18:36:57
    容器可以说是Java Core中比较重要的一部分了。 数组,String,java.util下的集合容器 ============================================================================== 数组长度限制为 Integer.Integer.MAX_...
  • Java线程之集合类(浅析线程安全和不安全) 本文目录: 1.线程安全之ArrayList,HashSet,HashMap和线程安全之CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap 2. 小结 3.解析...
  • Java常见的线程安全的类

    万次阅读 2017-04-12 17:40:54
    通过synchronized 关键字给方法加上同步的内置锁来实现线程安全 Timer,TimerTask,Vector,Stack,HashTable,StringBuffer 原子类Atomicxxx—包装类的线程安全类 如AtomicLong,AtomicInteger等等,Atomicxxx ...
  • 不可变对象天生就是线程安全的。它们的常量(变量)是在构造函数中创建的,既然它们的状态无法被修改,那么这些常量永远不会被改变——不可变对象永远是线程安全的。 不可变性的理解: 无论是Java语言规范还是Java...
  • Java线程编程-(1)-线程安全和锁Synchronized概念

    万次阅读 多人点赞 2017-09-15 14:51:59
    一、进程与线程的概念 (1)在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。 在未配置 OS 的系统中,程序的执行方式是顺序执行,即必须在一个程序执行完后,才允许另一个...
  • java线程安全之并发Queue(十三)

    万次阅读 2017-11-19 23:40:23
    java线程安全之并发Queue(十三)
  • Java线程之同步类容器与并发容器

    千次阅读 2017-06-09 14:03:32
    同步容器类同步容器类包括Vector和HashTable,而且是早期JDK的一部分,这些同步的封装器类是由Collections.synchronizedxxx等工厂方法创建的。例如 Map,String> map =...HashMap本来不是线程安全的,但是上面的map
  • java线程安全和锁优化

    千次阅读 2016-04-10 16:17:43
    线程安全的一个恰当的定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果...
  • Java并发:线程安全与锁优化

    千次阅读 2018-01-01 18:25:15
    概述人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情。...1.线程安全Java Co...
  • JAVA中ArrayList如何保证线程安全

    万次阅读 2016-11-03 11:17:35
    保证线程安全的三种方法: 不要跨线程访问共享变量 使共享变量是final类型的 将共享变量的操作加上同步 一开始就将类设计成线程安全的, 比在后期重新修复它,更容易. 编写多线程程序, 首先保证它是正确的, 其次再...
  • 1.ArrayList 线程安全验证 验证Demo: /** * 集合类ArrayList线程安全验证 * * @author wangjie * @version V1.0 * @date 2019/12/17 */ public class ContainerNotSafe { public static void main...
  • 1. Java中的线程安全 Java线程安全:狭义地认为是多线程之间共享数据的访问。 Java语言中各种操作共享的数据有5种类型:不可变、绝对线程安全、相对线程安全线程兼容、线程独立 ① 不可变 不可变...
  • 但如果亲自操刀写一个单例的类,怎么写最完美,并且能保证线程安全(在代码层面上控制类在内存中不可能出现多于1个对象)呢。 我们都知道Java单例设计模式分饿汉式和懒汉式。 饿汉式即不管 有没有人用到这个对象,...
  • Java线程线程安全的集合

    千次阅读 2020-10-18 21:56:28
    线程安全的集合 Vector Vector集合是对ArrayList集合线程安全的实现,它们两者在方法的实现上没有什么太大的区别,最大的区别就是,Vector在方法前面加上了synchronized关键字,用于保证线程安全。 【Java集合框架...
  • java线程安全之同步类容器与并发类容器(十二)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 244,663
精华内容 97,865
关键字:

以下哪些java容器是线程安全

java 订阅