本文共 2126 字,大约阅读时间需要 7 分钟。
在并发编程中有两个核心问题, 一个是互斥另一个是同步。在java sdk并发包中,lock就是解决互斥问题,conditon就是解决同步问题。
lock有三个重要的特性:
// 支持中断的 APIvoid lockInterruptibly() throws InterruptedException;// 支持超时的 APIboolean tryLock(long time, TimeUnit unit) throws InterruptedException;// 支持非阻塞获取锁的 APIboolean tryLock();
可以先看代码
class X { private final Lock rtl = new ReentrantLock(); int value; public void addOne() { // 获取锁 rtl.lock(); try { value+=1; } finally { // 保证锁能释放 rtl.unlock(); } }}
这段代码,如果T1线程将value改为1 那么t2线程在执行的时候能够看见吗?
答案是肯定的。 它是利用了volatile相关的happes-before规则。java sdk里面的ReentrantLock,内部持有一个volatile的成员变量state,获取锁的时候,会读写 state的值;解锁的时候,也会读写state,在执行value+=1之后,又读写了一次volatile变量state。根据相关的Happens-before规则:class SampleLock { volatile int state; // 加锁 lock() { // 省略代码无数 state = 1; } // 解锁 unlock() { // 省略代码无数 state = 0; }}
ReentrantLock,指的是线程可以重复获取同一把锁。例如下面的代码,当线程T1执行到1处时,已经获取到了锁rtl,当在1处调用get()方法时,会在2处再次对锁rtl执行加锁操作。如果锁rtl是可重入的,那么线程T1可以再次加锁成功。如果锁rtl是不可重入的,那么线程T1此时会被阻塞。
class X { private final Lock rtl = new ReentrantLock(); int value; public int get() { // 获取锁 rtl.lock(); ② try { return value; } finally { // 保证锁能释放 rtl.unlock(); } } public void addOne() { // 获取锁 rtl.lock(); try { value = 1 + get(); ① } finally { // 保证锁能释放 rtl.unlock(); } }}
ReentrantLock有两个构造方法。一个是无参构造,一个是传入fair。fair参数代表的是锁的公平策略,如果传入 true就表示需要构造一个公平锁,反之则表示构造一个非公平锁。
// 无参构造函数:默认非公平锁public ReentrantLock() { sync = new NonfairSync();}// 根据公平策略参数创建锁public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync();}
公平锁就是先等待先唤醒,非公平锁是随机唤醒。
这三条中最后一条主要是担心在调用慢方法,如长时间IO或者业务复杂。 也会担心其他方法里面本身有锁,在调用的时候加锁会出现双重锁, 容易导致死锁。
转载地址:http://bnkws.baihongyu.com/