CopyOnWriteArraySet<E>和CopyOnWriteArrayList<E>
CopyOnWrite ArraySet<E>类图
CopyOnWriteArrayList<E>
CopyOnWriteArrayList<E>类是ArrayList<E>的线程安全版本。一句话很清楚的说明了CopyOnWriteArrayList<E>的作用,注释中同样指出了实现了原理:任何改变数组的操作都是在内部数组的一个新的拷贝上进行的。这样的实现对于写来说代价很大,但如果多线程环境下遍历的操作,读操作远远多于写操作来说就比较高效了。因此应用的场景就是:在多线程环境下一个读操作远远多于写操作的列表。
线程安全最简单的做法就是同步,那我们从源码的角度来看下ConpyOnWriteArrayList<E>相对于直接在ArrayList<E>上进行操作的同步比较起来的改进之处:写操作在可重入锁的保护下在内部数组的拷贝上进行,操作完成后将内部数组换成最新的数组。这样读操作就不许要进行同步,避免了直接同步ArrayList<E>时对读操作带来的开销。
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } //读操作不做同步保护 public E get(int index) { return get(getArray(), index); } |
Iterator操作
CopyOnWriteArrayList<E>的Iterator返回的迭代器是COWIterator<E>,实际迭代的内容是调用Iterator是列表的一个快照,因此可以很快的迭代整个序列,但是因为是快照,所以无法通过迭代器的写操作来改变原先的列表,所以该迭代器的写操作都会抛出异常,如:
private COWIterator(Object[] elements, int initialCursor) { cursor = initialCursor; snapshot = elements; } //COWIterator的remove,set,add操作都会抛出异常 public void remove() { throw new UnsupportedOperationException(); } |
CopyOnWriteArraySet<E>
CopyOnWriteArraySet<E>内部的本质使用的就是CopyOnWriteArrayList<E>再实现java.util.Set接口来实现Set的操作,因此同样具有以下特点:
1. 适用于数量不大,读操作远远多于写操作,需要多线程安全的。
2. 写操作的代价非常高。
3. 迭代器Iterator不支持remove等写操作,带迭代器的读很快。
private final CopyOnWriteArrayList<E> al;
/** * Creates an empty set. */ public CopyOnWriteArraySet() { al = new CopyOnWriteArrayList<E>(); }
public boolean add(E e) { return al.addIfAbsent(e); } public Iterator<E> iterator() { return al.iterator(); } |
具体应用
Java.sql.DriverManager中用来保存注册过的所有Driver信息。该应用场景实际上就是依次注册,后此多次遍历使用的场景,同时需要提供线程安全的特性。
Java.sql.DriverManager:
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
// Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { /* * When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, so that the JDBC driver class outside rt.jar * can be loaded from here. */ ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized (DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } }
if(url == null) { throw new SQLException("The url cannot be null", "08001"); }
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; //遍历注册的驱动信息,获取url对应的驱动类。 for(DriverInfo aDriver : registeredDrivers) { // If the caller does not have permission to load the driver then // skip it. if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } }
} else { println(" skipping: " + aDriver.getClass().getName()); }
}
// if we got here nobody could connect. if (reason != null) { println("getConnection failed: " + reason); throw reason; }
println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); }
|
Com.mysql.jdbc.Driver:
//如com.mysql.jdbc.Driver类启动时会调用DriverManager.registerDriver方法来注册mysql //的驱动信息 package com.mysql.jdbc;
import java.sql.DriverManager; import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } } |