JUC包工具类介绍二
异步任务
Callable
Callable接口定义一个异步任务,当Callable接口提交到ExecutorService进行异步执行时,返回结果通过Java Future获取。Callable接口同样可以获取任务执行时的异常。
public class MyCallable implements Callable<String> { @Override public String call() throws Exception { return String.valueOf(System.currentTimeMillis()); } } |
Callable 和 Runnable的区别:
两者都表示一个可以被其他线程执行的任务。Runnable的run方法没有返回值,任务发起线程也无法获取到任务执行中的已检查异常,只能获取到RuntimeException.而Callable可以获取返回值和异常。两者的设计上Runable是为了长时间的并发任务而设计,如网络连接,监听文件等。而Callable适合于返回一个结果的单次任务。
Java Future
Future表示一个异步计算的结果。当异步任务创建之后如Callable提交给ExecutorService之后,返回Future对象,Future接口提供获对异步任务的交互方法,如获取结果,取消任务等,检查任务状态。
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning) V get(); V get(long timeout, TimeUnit unit); boolean isCancelled(); boolean isDone(); } |
锁
锁用于避免资源竞争带来的异常。相比于Synchronized,可以提供线程资源唤醒顺序(公平锁),更细粒度的作用于(Synchronized只能作用于整个方法,Lock通过Lock和unLock方法控制),更丰富的控制(如等待超时等参数)
ReentrantLock
ReentrantLock是一个可重入锁的实现, 可重入锁表示持有锁的现成可以重新获取对锁进行锁定,相应的多次的锁定需要进行多次的unLock。
Lock lock = new ReentrantLock(); lock.lock(); //critical section lock.unlock(); |
公平锁: 公平锁是指等待锁的现成获取锁的顺序按照等待的顺序进行,而非公平锁表示不保证获取顺序。ReentrantLock创建时可以指定是否公平锁。公平锁必须使用带超时的tryLock方法。
ReadWriteLock
读写锁则是针对读写业务特性设计的锁。保证多线程可以同时读,但当存在写操作时,只能有一个线程持有,此时读操作也无法获取锁。
Read Lock: 如果当前没有写锁被占有,并且没有写锁的请求,则多线程可以获取读锁进行读操作。
Write Lock: 如果当前没有现成在进行读或写,只有一个线程可以锁定写锁。
JUC 下的读写锁实现:ReentrantReadWriteLock
ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); readWriteLock.readLock().lock(); readWriteLock.readLock().unlock(); readWriteLock.writeLock().lock(); readWriteLock.writeLock().unlock(); |
Atomic系列
Atomic系列对象提供了针对特定类型提供原子性操作的操作方法。主要是针对于单一操作因为Java字节码分析后实际是多个操作执行的原因,在并发环境下结果可能异常的情况。
AtomicBoolean\ AtomicInteger\ AtomicLong
提供了可以原子性对boolean变量进行读写操作,已经compareAndSet等高作操作的原子性。
AtomicBoolean atomicBoolean = new AtomicBoolean(true); boolean value = atomicBoolean.get(); atomicBoolean.set(false); boolean oldValue = atomicBoolean.getAndSet(false); boolean expectedValue = true; boolean newValue = false; boolean wasNewValueSet = atomicBoolean.compareAndSet( expectedValue, newValue); |
AtomicInteger和AtomicLong类型类似。
AtomicReference\AtomicStampedReference
AtomicReference 保证多线程对对象引用改变不会出现一个不一致的状态。
AtomicReference atomicReference = new AtomicReference();
atomicReference.set("New object referenced"); boolean exchanged = atomicStringReference.compareAndSet(initialReference, newReference); |
AtomicStampedReference则是为解决ABA问题而设计的。 ABA问题指的是当前对象是A,经过改变设置为B之后,又重新设置为A, 使用传统的compareAndSet方法进行设置时无法意识到A-B-A的改变过程而误认为对象一直为A没有改变过,从而进行了错误的操作。
AtomicStampedReference对引用的状态添加了时间戳的概念, 对象的完整状态必须包括对象本身以及设置时候的时间戳,因此即使是同一个值,不同的时间戳也就表示不同的状态。
String initialRef = "initial value referenced"; int initialStamp = 0; AtomicStampedReference<String> atomicStringReference = new AtomicStampedReference<String>( initialRef, initialStamp ); String newRef = "new value referenced"; int newStamp = initialStamp + 1; boolean exchanged = atomicStringReference .compareAndSet( initialRef, newRef, initialStamp, newStamp); System.out.println("exchanged: " + exchanged); //true exchanged = atomicStringReference .compareAndSet( initialRef, "new string", newStamp, newStamp + 1); System.out.println("exchanged: " + exchanged); //false exchanged = atomicStringReference .compareAndSet( newRef, "new string", initialStamp, newStamp + 1); System.out.println("exchanged: " + exchanged); //false exchanged = atomicStringReference .compareAndSet( newRef, "new string", newStamp, newStamp + 1); System.out.println("exchanged: " + exchanged); //true |
AtomicIntegerArray\AtomicLongArray\AtomicReferenceArray
AtomicIntegerArray 数组对象系列表示可以进行原子操作的数组。包括AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。
int[] ints = new int[10]; ints[5] = 123; AtomicIntegerArray array = new AtomicIntegerArray(ints); int value = array.get(5); array.set(5, 999); boolean swapped = array.compareAndSet(5, 999, 123); int newValue = array.addAndGet(5, 3); |
CopyOnWriteArrayList\ CopyOnWriteArraySet
提供线程安全的ArrayList对象,实现上将对ArrayList的所有更新操作都作用在一个写副本上,并且通过ReentrantLock来保护并发更新。Set方法的源码如下:
public E set(int index, E element) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); E oldValue = get(elements, index); if (oldValue != element) { int len = elements.length; //拷贝副本进行更新操作,并将新的副本设置为最新列表 Object[] newElements = Arrays.copyOf(elements, len); newElements[index] = element; setArray(newElements); } else { // Not quite a no-op; ensures volatile write semantics setArray(elements); } return oldValue; } finally { lock.unlock(); } } |
CopyOnWriteArraySet提供线程安全的Set操作,底层使用CopyOnWriteArrayList实现。