为了账号安全,请及时绑定邮箱和手机立即绑定

Java线程与并发编程实践----额外的并发工具类

标签:
Java

一、并发集合

    java.util包下提供了很多的集合类,如ArrayList、TreeSet、HashMap,但是这些

集合都是非线程安全的,并且对于单列集合的迭代器,采用的是快速失败机制,当正在迭代

遍历的集合被其它线程修改时,便会抛出 java.util.ConcurrentModificationException。

这显然对于多线程操作的集合是十分不方便的,但早Colections这个工具类中有方法可以返回

线程安全的集合,然而这种集合对于高并发环境,性能十分低下。

    于是,java.util.concurrent包下提供了很多相关集合的类,即并发集合,这些集合

具有并发性能和高扩展性,并且返回的是弱一致性的迭代器,即不再是快速失败机制的迭代器。

大致描述如上,具体翻查jdk。下面单独研究使用并发集合完成生产者消费者的问题。

使用BlockingQueue和ArrayBlockingQueue实现生产者消费者:

ArrayBlockingQueue是一个由数组支的有界阻塞队列,当队列满载时,put()方法会阻塞,当

队列为空时,take()方法阻塞,因此用这种队列可以很轻松的实现消费者、生产者。

转到http://blog.51cto.com/12222886/1963884

深入学习ConcurrentHashMap:


重点了解此方法。

ConcurrentHashMap是一个线程安全的集合,但是在多线程德操作之下它并不是线程安全

的,例如如下代码:

if(!map.containsKey("something")) 			map.put("something", "something");

试想,当我们再判断完之后,还未put之前,有一条线程向map中put进一个键为something的值,

然后我们再继续put,那么刚才的那个值是不是就被覆盖了?这就产生了安全问题。。为了保证

安全我们应该这样做:

synchronized (map) {			if (!map.containsKey("something"))				map.put("something", "something");		}

以上可以解决安全问题,但是性能会被降低。。我们应该使用ConcurrentHashMap所提供的

一个API,putIfabsent(K key,V value),它的含义是,假如键值不存在,便put进去,它是线程

安全的,并且性能更高,相当于如下代码:

synchronized (map) {			if (!map.containsKey("something"))				return map.put("something", "something");			else				return map.get(key);		}

二、原子变量

    和对象监听器关联的那些内置锁一直以来都有性能不佳的问题,后来出现了

很多非阻塞算法,可大大提高性能和扩展性。

    java.util.concurrent.atomic提供了高效非阻塞算法。它支持单个变量可进行无

锁及线程安全的操作。有如下原子类:


原子变量用于实现计数器、序列生成器以及其它构造。在线程高争用的环境下,这些构造要求互斥

而不影响性能。假如有如下代码:

package xiancheng;public class ID {	private static volatile long nextID = 1;		static synchronized long getNextID() {		return nextID++;	}}

上述代码中volatile保证了可见性,synchronized保证了互斥性,在多线程环境下,上述代码没有

问题,然而在高争用的环境中性能会很低。我们可以用原子变量代替上述代码:

class ID2{	private static AtomicLong nextID = new AtomicLong();		public static long getNextID() {		return  nextID.getAndIncrement();	}}

上述代码完全保证了可见性,互斥性以及操作的原子性,并且由于它实现了高争用下的非阻塞算法,

因此它的性能相对来说高了很多。

那么问题来了,为什么原子变量能够提高性能????

Compare-and-swap(CAS机制)

    Compare-and-swap(CAS机制)是一个针对非抢占式微处理器的一条指定指令的宽泛术语,这条

指令读取内存的位置,比较读到的值和期望的值,当读到值和期望值匹配时,就将新值存储到该内存

位置,否则,什么也不发生。

CAS支持原子的读-改-写序列,通常这样使用:

    (1)从地址A读取x

     (2)在x进行多步计算

     (3)使用CAS将A值从x变为y。在进行这些操作时,如果A值没有改变,CAS就成功了

那CAS到底优越之处在哪呢?

package xiancheng;public class ID {	private static volatile long nextID = 1;		static synchronized long getNextID() {		return nextID++;	}}

上面的代码使用synchronized,高争用环境下的监听锁会导致过多的上下文切换,这样会阻碍所有线程

并且导致应用程序无法很好的扩展。而CAS机制不适用监听器来是操作原子化,而是在修改ID的值之前会

进行判断,如果该值没发生过变化,就将新值赋值给该变量,如果发生变化了就什么也不做,而在这中间

对值是否发生过变化的判断是利用CAS指令完成的。

java.util.concurrent.locks.ReentrantLock就使用了CAS机制改善了性能,原子类也利用了CAS机制。


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消