文章目录
  1. 第五章 Java中的锁
    1. 5.1 Lock接口
    2. 5.2 队列同步器(AQS)-面向自定义锁的实现者
      1. 1、同步队列
      2. 2、独占式同步状态获取与释放
      3. 3、一个示例,TwinsLock
    3. 5.3 重入锁
    4. 5.4 读写锁
    5. 5.5-5.6 Condition接口

第五章 Java中的锁

5.1 Lock接口

1、可以尝试非阻塞获取锁(lockInterruptibly())
2、能被中断地获取锁
3、超时获取锁

5.2 队列同步器(AQS)-面向自定义锁的实现者

1、同步队列

同步队列的基本结构

  • 设置尾节点为原子方式
  • 头节点线程释放同步状态后,唤醒后继结点,由于只有一个线程能够获取到同步状态(当前的头节点),所有设置头节点不需要CAS保证

2、独占式同步状态获取与释放

通过几个死循环的方式保证节点添加的并发请求串行化
节点自旋获取同步状态

  • 节点进入同步队列后,自省观察并获取同步状态,过程中头节点不为空,enq方法保证,第一次enq方法后(enq中会有两次循环),队列状态如下:
    入参节点node的前驱是一个新节点,在方法enq中new出,此时新的线程在公平锁的获取流程中,hasQueuedPredecessors()返回ture,后继节点node再继续获取锁的流程中(注意此时图中node的线程并没有返回,因而获取锁过程没有结束),多个线程竞争依靠compareAndSetState的CAS方式保证一致性。
    同步队列的初始化
    自省的实现方式-死循环
    独占式获取同步状态的流程

    释放调用release(int)方法进行释放,其中唤醒后继节点

    3、一个示例,TwinsLock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package concurrent.concurrentArt.chapter5;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 一种基于AQS的自定义锁,最多两个线程进入
* Created with IntelliJ IDEA.
* User: pingansheng
* Date: 2016/8/26
* Time: 11:13
*/
public class TwinsLock implements Lock {
private final Sync sync = new Sync(2);
private static class Sync extends AbstractQueuedSynchronizer {
Sync(Integer count) {
if (count < 0) {
throw new IllegalArgumentException("count must larger than zero.");
}
setState(count);
}
@Override protected int tryAcquireShared(int reduceCount) {
for (; ; ) {
int current = getState();
int newCount = current - reduceCount;
if (newCount < 0 || compareAndSetState(current, newCount)) {
return newCount;
}
}
}
@Override protected boolean tryReleaseShared(int returnCount) {
for (; ; ) {
int current = getState();
int newCount = current + returnCount;
if (compareAndSetState(current, newCount)) {
return true;
}
}
}
}
@Override public void lock() {
sync.acquireShared(1);
}
@Override public void unlock() {
sync.releaseShared(1);
}
@Override public void lockInterruptibly() throws InterruptedException {
}
@Override public boolean tryLock() {
return false;
}
@Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override public Condition newCondition() {
return null;
}
}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package concurrent.concurrentArt.chapter5;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
/**
* 验证twinsLock
* Created with IntelliJ IDEA.
* User: pingansheng
* Date: 2016/8/30
* Time: 14:05
*/
public class TwinsLockTest {
public static void main(String[] args) throws Exception{
final Lock lock = new TwinsLock();
class Worker extends Thread {
public void run() {
while (true) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
} finally {
lock.unlock();
}
break;
}
}
}
for(int i=0;i<10;i++){
Worker w=new Worker();
// w.setDaemon(true);
w.start();
}
for(int i=0;i<10;i++){
Thread.sleep(1000);
System.out.println();
}
}
}

5.3 重入锁

1、获取同步状态时,如果该线程与当前持有线程相同,则进行同步状态值增加返回true,表示获取成功
2、公平锁与非公平锁:前者FIFO(获取锁时会检查当前同步队列时候有等待的线程节点,见上文红色),后者CAS竞争,前者代价是进行大量的线程切换(切换锁),非公平锁可能造成饥饿,但是线程切换很少(耗时94倍,切换次数133倍)

5.4 读写锁

1、同一时刻允许多个读线程访问,写线程访问时,读写均阻塞
2、一般情况下,读写锁的性能都会比排它锁好,大多数情景读是多于写的
3、读写锁实现分析:整形按位切割,32位前16位用于标记读状态,后16位标记写状态,写状态=S & 0X0000FFFF,读状态=S>>>16,所以,S不等于0时候,写状态=0则读状态>0,即表示读锁已经获取
4、锁降级:把持住写锁,再获取到读锁,随后释放写锁

5.5-5.6 Condition接口

1、Condition对象:依赖Lock接口实现一套等待通知机制
2、一个示例,有界队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package concurrent.concurrentArt.chapter5;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created with IntelliJ IDEA.
* User: pingansheng
* Date: 2016/9/8
* Time: 16:53
*/
public class ConditionTest {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws Exception {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
}
public void conditionSignal() throws Exception {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws Exception {
final BoundedQueue<Integer> queue = new BoundedQueue<>(10);
new Thread() {
@Override public void run() {
try {
while (true) {
System.out.println("remove:" + queue.remove());
}
} catch (Exception e) {
}
}
}.start();
while (true) {
queue.add(1);
System.out.println("add:"+1);
}
}
private static class BoundedQueue<T> {
private Object[] items;
private int addIndex, removeIndex, count;
private Lock lock = new ReentrantLock();
private Condition notEmpty = lock.newCondition();
private Condition notFull = lock.newCondition();
public BoundedQueue(int size) {
items = new Object[size];
}
public void add(T t) throws Exception {
lock.lock();
try {
while (count == items.length) {
notFull.await();
}
items[addIndex] = t;
if (++addIndex == items.length) {
addIndex = 0;
}
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T remove() throws Exception {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
Object x = items[removeIndex];
if (++removeIndex == items.length) {
removeIndex = 0;
}
--count;
notFull.signal();
return (T) x;
} finally {
lock.unlock();
}
}
}
}

3、如果一个线程调用Condition.await(),则其释放锁、构造为节点加入等待队列并进入等待状态
4、等待await:同步队列中的首节点构造为新节点,加入等待队列,等待队列新增节点的线程不需要CAS,因为调用await()的线程必定获取了锁,由锁来保证线程安全
5、通知signal:一起唤醒唤醒等待时间最长的,即首个等待节点,加入同步队列中由locksupport唤醒节点中的线程
Condition的结构与等待通知示意图

文章目录
  1. 第五章 Java中的锁
    1. 5.1 Lock接口
    2. 5.2 队列同步器(AQS)-面向自定义锁的实现者
      1. 1、同步队列
      2. 2、独占式同步状态获取与释放
      3. 3、一个示例,TwinsLock
    3. 5.3 重入锁
    4. 5.4 读写锁
    5. 5.5-5.6 Condition接口