本文章是Java多线程系列的一篇文章,其他文章:
Java多线程:锁的底层实现
Java多线程:synchronized和volatile
Java多线程:JUC包-锁的封装
Java多线程:Thread的使用,以及wait(),notify(),notifyAll()
Java多线程:线程池
之前在Java多线程:synchronized和volatile这篇文章中,讲了Java在语言层面上对对多线程提供的支持。那么,在实际应用中,很多时候仅仅靠这两个关键字是不够的。那么如果我们想进行更骚的操作,就需要使用 java.util.concurrent
包下的工具类了。这个包的结构如下所示:
- java.util
- atomic //原子操作
- locks //锁
本文将对其中的几个关键类进行介绍,其余的信息可以在参考链接里找到
Lock 接口支持那些语义不同(重入、公平等)的锁规则。所谓语义不同,是指锁可是有“公平机制的锁”、”非公平机制的锁”、”可重入的锁”等等。
//@since 1.5
public interface Lock {
//获取锁,失败则阻塞
void lock();
//Acquires the lock unless the current thread is interrupted.
void lockInterruptibly() throws InterruptedException;
//Acquires the lock only if it is free at the time of invocation.
boolean tryLock();
/**
* @param time the maximum time to wait for the lock
* @param unit the time unit of the time argument
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
ReadWriteLock 接口以和Lock类似的方式定义了一些读取者可以共享而写入者独占的锁。JUC包只有一个类实现了该接口,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。
public interface ReadWriteLock {
//Returns the lock used for reading.
Lock readLock();
//Returns the lock used for writing.
Lock writeLock();
}
Condition需要和Lock联合使用,它的作用是代替Object监视器方法,可以通过await(),signal()来休眠/唤醒线程。Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。
//nano 指的是nanoSecond,纳秒
//@since 1.5
public interface Condition {
//Causes the current thread to wait until it is signalled or interrupted.
void await() throws InterruptedException;
//Causes the current thread to wait until it is signalled.
void awaitUninterruptibly();
//Causes the current thread to wait until it is signalled or interrupted,or the specified waiting time elapses.
long awaitNanos(long nanosTimeout) throws InterruptedException;
//Causes the current thread to wait until it is signalled or interrupted,or the specified deadline elapses.
boolean awaitUntil(Date deadline) throws InterruptedException;
//Wakes up one waiting thread.
void signal();
//Wakes up all waiting threads.
void signalAll();
}
AbstractQueuedSynchronizer就是被称之为AQS的类,它是一个非常有用的超类,可用来定义锁以及依赖于排队阻塞线程的其他同步器;
ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier和Semaphore等这些类都是基于AQS类实现的。AbstractQueuedLongSynchronizer 类提供相同的功能但扩展了对同步状态的 64 位的支持。两者都扩展了类 AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。
LockSupport的功能和”Thread中的Thread.suspend()和Thread.resume()有点类似”,LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程。但是park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。
ReentrantLock是独占锁。所谓独占锁,是指只能被独自占领,即同一个时间点只能被一个线程锁获取到的锁(互斥同步语义)。ReentrantLock锁包括”公平的ReentrantLock”和”非公平的ReentrantLock”(公平非公平语义)。”公平的ReentrantLock”是指”不同线程获取锁的机制是公平的”,而”非公平的 ReentrantLock”则是指”不同线程获取锁的机制是非公平的”,ReentrantLock是”可重入的锁”(可重入语义)。
ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。
AtomicLong是通过对单一变量进行CAS操作来实现原子操作的。那么在高并发的场景下,就会导致大量进程CAS操作失败,造成资源的浪费。而LongAdder另辟蹊径,将单一value的CAS操作分担到多个value中去,降低单个value的 ‘热度‘ ,这样就能有效减少CAS失败带来的开销。
LongAdder的作者是Doug Lea
,Doug Lea对该类的说明是: 低并发时LongAdder和AtomicLong性能差不多,高并发时LongAdder更高效。实际的测试也证明了这一点。关于LongAdder的分析网上已经有很详细的文章,这里就不再赘述,可以参考如下的文章:
源码分析:LongAdder 空间换时间-比AtomicLong还高效的无锁实现
Benchmark:LongAdder vs AtomicLong
参考:
JUC包中的锁-系列文章
ReentrantLock和synchronized的区别