一、java.util.concurrent.atomic包
- 软件包 java.util.concurrent.atomic
- 类的小工具包,支持在单个变量上解除锁的线程安全编程。
- 原子操作(automic operation)即不能被线程调度机制中断的操作。原子操作不需要进行同步控制
//类摘要 AtomicBoolean 可以用原子方式更新的 boolean 值。 AtomicInteger 可以用原子方式更新的 int 值。 AtomicIntegerArray 可以用原子方式更新其元素的 int 数组。 AtomicIntegerFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。 AtomicLong 可以用原子方式更新的 long 值。 AtomicLongArray 可以用原子方式更新其元素的 long 数组。 AtomicLongFieldUpdater<T> 基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。 AtomicMarkableReference<V> AtomicMarkableReference 维护带有标记位的对象引用,可以原子方式对其进行更新。 AtomicReference<V> 可以用原子方式更新的对象引用。 AtomicReferenceArray<E> 可以用原子方式更新其元素的对象引用数组。 AtomicReferenceFieldUpdater<T,V> 基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。 AtomicStampedReference<V> AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。
二、java.util.concurrent.locks
- 软件包 java.util.concurrent.locks
- 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。
- 该框架允许更灵活地使用锁和条件,但以更难用的语法为代价。
Lock 接口支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock。 Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。 ReadWriteLock 接口以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实现,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。
三、java.util.concurrent.locks.ReadWriteLock
/** * ReadWriteLock * ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。 * 只要没有 writer,读取锁可以由多个 reader 线程同时保持。 * 写入锁是独占的。 * 所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。 * 也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。 * 与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。 * 虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。 * 从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。 * 在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。 * 与互斥锁相比,使用读-写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。 * 例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁的理想候选者。 * 但是,如果数据更新变得频繁,数据在大部分时间都被独占锁,这时,就算存在并发性增强,也是微不足道的。 * 更进一步地说,如果读取操作所用时间太短,则读-写锁实现(它本身就比互斥锁复杂)的开销将成为主要的执行成本,在许多读-写锁实现仍然通过一小段代码将所有线程序列化时更是如此 * 最终,只有通过分析和测量,才能确定应用程序是否适合使用读-写锁。 * 尽管读-写锁的基本操作是直截了当的,但实现仍然必须作出许多决策,这些决策可能会影响给定应用程序中读-写锁的效果。具体参考api */ public interface ReadWriteLock { //返回用于读取操作的锁 Lock readLock(); //返回用于写入操作的锁 Lock writeLock(); } 所有已知实现类: ReentrantReadWriteLock
四、示例
/** * 在使用某些种类的 Collection 时,可以使用 ReentrantReadWriteLock 来提高并发性。 * 通常,在预期 collection 很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。 * 例如,以下是一个使用 TreeMap 的类,预期它很大,并且能被同时访问。 */ class RWDictionary { private final Map<String, Data> m = new TreeMap<String, Data>(); private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); public Data get(String key) { r.lock(); try { return m.get(key); } finally { r.unlock(); } } public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } public Data put(String key, Data value) { w.lock(); try { return m.put(key, value); } finally { w.unlock(); } } public void clear() { w.lock(); try { m.clear(); } finally { w.unlock(); } } }
import java.util.concurrent.locks.*; import java.util.*; class ReadWriteLockTest { public static void main(String[] args) { final Queue3 q3 = new Queue3(); for(int i=0;i<3;i++) { new Thread(){ public void run() { while(true) q3.get(); } }.start(); new Thread(){ public void run() { while(true) q3.put(new Random().nextInt(10000)); } }.start(); } } } class Queue3 { private Object data = null; ReadWriteLock rwl = new ReentrantReadWriteLock(); public void get() { rwl.readLock().lock(); try{ System.out.println(Thread.currentThread().getName()+" is ready to read data..."); try{Thread.sleep(1000);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+" have read data: "+data); }finally{ rwl.readLock().unlock(); } } public void put(Object data) { rwl.writeLock().lock(); try{ System.out.println(Thread.currentThread().getName()+" is ready to write data..."); try{Thread.sleep(1000);}catch(Exception e){} this.data = data; System.out.println(Thread.currentThread().getName()+" have write data: "+data); }finally{ rwl.writeLock().unlock(); } } }
/** * 重入 * 此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。 * 此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。 * 锁降级 * 重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。 * 示例用法。下面的代码展示了如何利用重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理) */ class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock(); if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock(); rwl.writeLock().lock(); // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) { data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } }
//设计一个简单的缓存系统 class CacheDemo { public static void main(String[] args) { System.out.println("Hello World!"); } private Map<String,Object> cache = new HashMap<String,Object>(); public Object getData(String key) { Object value = cache.get(key); if(value == null) { synchronized(CacheDemo.class) { if(value == null) value = "abc";//实际代码应该是queryDB(); } } return value; } }
//设计一个简单的缓存系统 class CacheDemo { public static void main(String[] args) { } private Map<String,Object> cache = new HashMap<String,Object>(); private ReadWriteLock rwl = new ReentrantReadWriteLock(); private Lock r = rwl.readLock(); private Lock w = rwl.writeLock(); public Object getData(String key) { r.lock(); Object value = cache.get(key); if(value == null) { r.unlock(); w.lock(); { if(value == null) value = "abc";//实际代码应该是queryDB(); } r.lock(); w.unlock(); } r.unlock(); return value; } }