线程安全对象简介

基本的线程安全对象有Vector,HashTable,还有经过Collections.synchronizedCollection()方法包装的集合对象。Java并发包中提供的安全类型有ConcurentHashMap,ConcurentLinkedQueue,CopyOnWriteArrayList,还有BlockingQueue的实现类型。另外还有Java原子包中提供的AtomicInteger,AtomicLong等原子类型。


经过Collections.synchronizedMap()包装的HashMap是一个线程安全对象,如Map m = Collections. synchronizedMap(new HashMap())。Collections.synchronizedMap()会生成一个名为SynchronizedMap的Map,它使用委托将自己所有Map相关的功能交给传入的HashMap实现,而自己主要负责保证线程安全。实际上就是将HashMap相关的操作方法加上了synchronized关键字以保证线程安全,在并发级别不高时可以使用。

ConcurentHashMap

ConcurentHashMap是Java并发包提供的并发HashMap,它专门为并发进行了性能优化,更加适合多线程的场合。其内部实现原理是:它实现了一个Segement数组,对于每一个数组元素来讲,都相当于一个HashTable。在同一个segement上操作时,会加锁进行互斥同步。而在不同的segement上操作时则是无锁的。它引入了“多段锁”的概念,将锁的粒度从整个对象上细化到了具体每个segement数组的元素上,以此达到提升并发性能的目的。

ConcurentLinkedQueue

高并发队列使用链表作为其数据结构。ConcurentLinkedQueue是高并发环境下性能最好的队列。其队列节点用Node表示,对Node进行操作时使用了CAS操作。因为没有使用锁而是单纯使用CAS操作来保证线程安全,因此大大增加了设计和实现的难度,但是带来的好处是性能得到飞速提升。

CopyOnWriteArrayList

在很多应用场景中,读操作可能远远大于写操作,对于这种场景,希望看到的是读操作尽可能快,写即使慢一些也没有太大关系。

由于读操作根本不会修改原有的数据,因此对于每次读取都进行加锁是一种资源浪费。根据读写锁的思想,读锁和写锁之间确实也不冲突,但是读操作会受到写操作的阻碍,当写发生时,读就必须等待,否则可能读到不一致的数据。同理,读操作在进行时候写操作也必须等待。

为了将性能发挥到极致,并发包中提供了CopyOnWriteArrayList类,对它来说,读取完全不用加锁,而且写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待。这样一来,读操作的性能就会大幅度提升。其实现是:在写入操作的时候进行一次自我复制,将修改的内容写入副本中,写完之后再将修改完的副本替换原来的数据,这样就可以保证写操作不会影响读了。

BlockingQueue

假设有两个线程A和B,在某种模式下两个线程间需要进行通信,即A线程能够通知B,但是又希望A不知道B的存在。这样将来重构或者升级时完全可以不修改A而直接把线程B升级为线程C,保证系统的平滑过渡。这种情形下就可以使用BlockingQueue来实现消息通信。BlockingQueue是一个接口,而不是一个具体的实现。其主要的实现类有ArrayBlockQueue和LinkedBlockigQueue。前者基于数组实现,后者基于链表实现。也正为如此,ArrayBlockingQueue更适合做有界队列,因为队列中可容纳的最大元素需要在队列创建时指定。而LinkedBlockingQueue适合做无界队列,或者那些边界值很大的队列,因为其内部元素可以动态增加,也不会因为初值容量很大而一下子消耗掉大量内存空间。

BlockingQueue之所以适合作为数据共享的通道,其关键还在于Blocking上。Blocking是阻塞的意思,它让服务线程在队列为空时进行等待,当有新的消息进入队列后自动将线程唤醒。其实现阻塞的关键是:存入元素的put()方法和取元素的take()方法。put()方法将元素压入队列末尾,当队列满时就阻塞等待,直到队列中有空闲的位置。take()方法从队列中取出元素,当队列为空时就等待,直到队列中有可用元素。BlockingQueue使用非常普通,在生产者和消费者模型中,可以将其作为缓冲区来使用。

SkipList

在JDK的并发包中除了常用的哈希表外还实现了一种有趣的数据结构,那就是跳表。跳表是一种可以用来快速查找的数据结构,有点类似于平衡树。但有一个重要的区别,对平衡树的插入和删除往往可能导致平衡树进行一次全局的调整。对跳表的插入和删除只需要对整个数据结构的局部进行操作即可。这样带来的好处是,在高并发情况下,平衡树需要一个全局的锁来保证线程安全,而跳表值只需要局部锁即可,并发性能更好。

跳表使用多层链表实现,最底层的链表维护了跳表内所有的元素,每上面一层都是下面一层的子集。跳表内所有链表的元素都是排序的,查找时可以从顶级链表开始找,查找效率是O(logN)。

可以使用跳表实现Map,如ConcurentSkipListMap。用跳表实现的Map里所有的元素都是排序的,对跳表进行遍历时会得到一个有序的结果。跳表的节点用Node实现,对Node的所有操作均使用CAS方法。

原子类型

原子类型为Java的atomic包中提供的线程安全类,主要有AtomicInteger,AtomicLong,AtomicBoolean和AtomicReference等。这些类型的实现里面用到了CAS操作,通过非阻塞同步的方式实现了线程安全。具体实现可以去阅读源码。在jdk1.8中有个类型LongAdder可以替换AtomicInteger,因为与AtomicInteger相比,在并发量小时两者性能接近,并发量大时效率大大提高。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值