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机制。


原文地址:http://blog.51cto.com/12222886/2063613

时间: 2024-08-11 07:41:44

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

Java线程与并发编程实践----同步器(交换器、信号量)

一.交换器 交换器提供了一个线程之间能够交换对象的同步点.每个线程都会往这个 交换器的exchange()方法传入一些对象,匹配伙伴线程,同时接受伙伴对象作为返 回值.java.util.conurrent.Exchange<V>实现了交换器. 下面是一个代码小实例: import java.util.concurrent.Exchanger;   import java.util.concurrent.ExecutorService;   import java.util.concurren

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

Java.util.concurrent.locks包提供了一个包含多种接口和类的框架,它 针对条件进行加锁和等待.不同于对象的内置加锁同步以及java.lang.Object的等 待/通知机制,包含锁框架的并发工具类通过轮询锁.显示等待及其它方式改善这种 机制. 锁框架包含了经常使用的锁.重入锁.条件.读写锁以及冲入读写锁等类别. 一.锁(Lock) Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作.此实 现允许更灵活的结构,可以具有差别很大的属性,可以

[Java 并发] Java并发编程实践 思维导图 - 第二章 线程安全性

根据<Java并发编程实践>一书整理的思维导图.

[Java 并发] Java并发编程实践 思维导图 - 第一章 简介

阅读<Java并发编程实践>一书后整理的思维导图.

读Java并发编程实践中,向已有线程安全类添加功能--客户端加锁实现示例

在Java并发编程实践中4.4中提到向客户端加锁的方法.此为验证示例,写的不好,但可以看出结果来. package com.blackbread.test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public

[Java 并发] Java并发编程实践 思维导图 - 第六章 任务执行

根据<Java并发编程实践>一书整理的思维导图.希望能够有所帮助. 第一部分: 第二部分: 第三部分:

[Java 并发] Java并发编程实践 思维导图 - 第五章 基础构建模块

根据<Java并发编程实践>一书整理的思维导图.希望能够有所帮助. 第一部分: 第二部分:

[Java 并发] Java并发编程实践 思维导图 - 第四章 对象的组合

根据<Java并发编程实践>一书整理的思维导图. 第一部分: 第二部分:

[Java 并发] Java并发编程实践 思维导图 - 第三章 对象的共享

根据<Java并发编程实践>一书整理的思维导图. 第一部分: 第二部分: 第三部分: