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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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机制。


以上是关于Java线程与并发编程实践----额外的并发工具类的主要内容,如果未能解决你的问题,请参考以下文章

Java线程与并发编程实践----并发工具类与Executor框架

Java线程与并发编程实践----锁框架

Java线程与并发编程实践----同步器(倒计时门闩)

java线程与并发编程实践

Java 并发编程技术实践之路专栏导读

Java 并发编程技术实践之路专栏导读