Java 免锁容器

来源:互联网 发布:linux是数据库软件么 编辑:程序博客网 时间:2024/04/30 08:03
1.CopyOnWriteArrayList<E>

  ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的;在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全的执行。当修改完成时,一个原子性的操作将把新的数组换入,使得新的读取操作可以看到这个新的修改。这对于读操作远远多于写操作的应用非常适合。CopyOnWriteArrayList的好处之一就是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException;采用类似技术的类还有:CopyOnWriteArraySet,ConcurrentHashMap和ConcurrentLinkedQueue等,都允许并发的读取和写入。特别注意:在CopyOnWriteArrayList上获得的Iterator是不能进行set和remove操作的,否则会抛出异常;

import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteListTest {  public static void main(String args[]) {//List<String> list=Collections.synchronizedList(new ArrayList<String>());CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>();list.add("1");list.add("2");list.add("3");Iterator<String> it = list.iterator();list.add("4");list.add("5");while (it.hasNext()) {   System.out.print(it.next()+" ");    }Iterator<String> it1 = list.iterator();while (it1.hasNext()) {System.out.print(it1.next()+" ");   } }}/*output: 1 2 3 1 2 3 4 5 */
2.读写锁ReadWriteLock

  ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。读写锁相对于线程互斥的优势在于高效,它不会对两个读线程进行盲目的互斥处理,当读线程数量多于写线程尤其如此,当全是写线程时两者等效。与互斥锁相比,使用读-写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。ReentrantReadWriteLock是它的一个实现类。此锁最多支持 65535 个递归写入锁和 65535 个读取锁。试图超出这些限制将导致锁方法抛出 Error。

  • ReentrantReadWriteLock.ReadLock readLock():返回用于读取操作的锁;返回的锁有是Lock接口的实现,拥有一系列的阻塞或者非阻塞的加锁的lock方法。调用这些lock方法获取读取锁时,如果另一个线程没有保持写入锁,则获取读取锁并立即返回。如果另一个线程保持该写入锁,出于线程调度目的,将禁用当前线程,并且在获取读取锁之前,该线程将一直处于休眠状态。
  • ReentrantReadWriteLock.WriteLock writeLock() :返回用于写入操作的锁;调用该锁对应的lock方法获取写入锁时,如果另一个线程既没有保持读取锁也没有保持写入锁,则获取写入锁并立即返回,并将写入锁保持计数设置为 1。如果当前线程已经保持写入锁,则保持计数增加 1,该方法立即返回。如果该锁被另一个线程保持,出于线程调度的目的,将禁用当前线程,并且在获得写入锁之前,该线程将一直处于休眠状态,此时写入锁保持计数设置为 1。
  通常,在预期 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(); }    } }



原创粉丝点击