1.队列同步器(
AbstractQueuedSynchronizer
)
它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排
队工作
同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,
随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将
会调用使用者重写的方法。
2.通过AQS实现简单的独占锁
独占锁Mutex是一个自定义同步组件,它在同一时刻只允许一个线程占有锁。
Mutex中定义了一个静态内部类,该内部类继承了同步器并实现了独占式获取和释放同步状态。在
tryAcquire(int acquires)方法中,如果经过CAS设置成功(同步状态设置为1),则代表获取了同步状
态,而在tryRelease(int releases)方法中只是将同步状态重置为0。
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
|
package
com.tlk.chapter5;
import
java.util.concurrent.TimeUnit;
import
java.util.concurrent.locks.AbstractQueuedSynchronizer;
import
java.util.concurrent.locks.Condition;
/**
* @author tanlk
* @date 2017年5月12日下午4:28:47
*/
public
class
Mutex {
//静态内部类,自定义同步器
private
static
class
Sync
extends
AbstractQueuedSynchronizer{
private
static
final
long
serialVersionUID = 2170507079497692699L;
//是否处于占用状态
protected
boolean
isHeldExclusively() {
return
getState() ==
1
;
}
//当状态为0的时候获取锁
public
boolean
tryAcquire(
int
acquiers) {
if
(compareAndSetState(
0
,
1
)) {
setExclusiveOwnerThread(Thread.currentThread());
return
true
;
}
return
false
;
}
//释放锁,将状态设置为0
protected
boolean
tryRelease(
int
arg) {
if
(getState() ==
0
)
throw
new
IllegalMonitorStateException();
setExclusiveOwnerThread(
null
);
setState(
0
);
return
true
;
}
Condition newCondition(){
return
new
ConditionObject();
}
}
private
final
Sync sync =
new
Sync();
public
void
lock(){
sync.acquire(
1
);
}
public
boolean
tryLock(){
return
sync.tryAcquire(
1
);
}
public
void
unLock(){
sync.release(
1
);
}
public
Condition newCondition(){
return
sync.newCondition();
}
public
boolean
isLocked(){
return
sync.isHeldExclusively();
}
public
boolean
hasQueuedThreads(){
return
sync.hasQueuedThreads();
}
public
void
lockInterruptibly()
throws
InterruptedException{
sync.acquireInterruptibly(
1
);
}
public
boolean
tryLock(
long
timeout, TimeUnit timeUnit)
throws
InterruptedException{
return
sync.tryAcquireNanos(
1
, timeUnit.toNanos(timeout));
}
}
|
独占式同步状态获取和释放:在获取同步状态时,同步器维护一
个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;移出队列(或停止自
旋)的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时,同步器调用
tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点。
3.通过AQS实现简单的共享锁
和上面的独占锁实现方式类似,只是state的范围为0-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
|
package
com.tlk.chapter5;
import
java.util.concurrent.TimeUnit;
import
java.util.concurrent.locks.AbstractQueuedSynchronizer;
import
java.util.concurrent.locks.Condition;
import
java.util.concurrent.locks.Lock;
public
class
TwinsLock
implements
Lock{
private
final
Sync sync =
new
Sync(
2
);
private
static
final
class
Sync
extends
AbstractQueuedSynchronizer{
/**
*
*/
private
static
final
long
serialVersionUID = 5161111893091476834L;
Sync(
int
count){
if
(count <
0
) {
throw
new
IllegalArgumentException(
"count must large than zero."
);
}
setState(count);
}
/**
* 默认state=2,获取的时候,state减需要获取的数量,返回剩余可获取的数量,如果小于0获取失败
* @param reduceCount
* @return
*/
@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 {
// TODO Auto-generated method stub
return
false
;
}
@Override
public
Condition newCondition() {
// TODO Auto-generated method stub
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
47
|
package
com.tlk.chapter5;
import
java.util.concurrent.locks.Lock;
import
org.junit.Test;
import
com.tlk.chapter4.SleepUtils;
/**
* 测试TwinsLock,每次至多有两个线程获得锁
* @author tanlk
* @date 2017年5月15日下午5:50:35
*/
public
class
TwinsLockTest {
@Test
public
void
test(){
final
Lock lock =
new
TwinsLock();
class
Worker
extends
Thread{
@Override
public
void
run() {
lock.lock();
try
{
SleepUtils.second(
1
);
System.out.println(Thread.currentThread().getName());
SleepUtils.second(
2
);
lock.unlock();
}
catch
(Exception e) {
lock.unlock();
}
}
}
//启动10个线程
for
(
int
i =
0
; i <
10
; i++) {
Worker w =
new
Worker();
w.setDaemon(
true
);
w.setName(
"thread-"
+i);
w.start();
}
//每隔一秒换行
for
(
int
i=
0
; i<
10
; i++){
SleepUtils.second(
1
);
System.out.println(i+
"---------------"
);
}
}
}
|
4.
重入锁ReentrantLock
当一个线程调用代码示例Mutex的lock()方法
获取锁之后,如果再次调用lock()方法,则该线程将会被自己所阻塞。所以引入了重入锁,重入锁
成功获取锁的线程再次获取锁,只是增加了同步状态值,这也就要求ReentrantLock在释放同步
状态时减少同步状态值,同步状态值可以为0-n,但大于0时
exclusiveOwnerThread是同一个。
ReentrantLock有公平性锁(fair)和非公平性锁(unfair),
公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成
线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量
5.读写锁
ReentrantReadWriteLock
1)读写锁,读状态写状态还是都维护在state上面,如果在一个整型变量上维护多种状态,就一定需要“按位切割使用”这个变量,读写锁将变量切
分成了两个部分,高16位表示读,低16位表示写,所以读写锁最大持有量是65535
jdk具体源码:
sharedCount:共享读锁数量
exclusiveCount:互斥写锁数量
1
2
3
4
5
6
7
8
9
|
static
final
int
SHARED_SHIFT =
16
;
static
final
int
SHARED_UNIT = (
1
<< SHARED_SHIFT);
static
final
int
MAX_COUNT = (
1
<< SHARED_SHIFT) -
1
;
static
final
int
EXCLUSIVE_MASK = (
1
<< SHARED_SHIFT) -
1
;
/** Returns the number of shared holds represented in count */
static
int
sharedCount(
int
c) {
return
c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
static
int
exclusiveCount(
int
c) {
return
c & EXCLUSIVE_MASK; }
|
读写锁使用代码简单示例:
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
|
package
com.tlk.chapter5;
import
java.util.HashMap;
import
java.util.Map;
import
java.util.concurrent.locks.Lock;
import
java.util.concurrent.locks.ReentrantReadWriteLock;
public
class
Cache {
static
Map<String, Object> map =
new
HashMap<String, Object>();
static
ReentrantReadWriteLock rwl =
new
ReentrantReadWriteLock();
static
Lock r = rwl.readLock();
static
Lock w = rwl.writeLock();
// 获取一个key对应的value
public
static
final
Object get(String key) {
r.lock();
try
{
return
map.get(key);
}
finally
{
r.unlock();
}
}
// 设置key对应的value,并返回旧的value
public
static
final
Object put(String key, Object value) {
w.lock();
try
{
return
map.put(key, value);
}
finally
{
w.unlock();
}
}
// 清空所有的内容
public
static
final
void
clear() {
w.lock();
try
{
map.clear();
}
finally
{
w.unlock();
}
}
}
|
6.
LockSupport工具
当需要阻塞或唤醒一个线程的时候,都会使用LockSupport工具类来完成相应工作。
LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而
LockSupport也成为构建同步组件的基础工具。
主要方法:
park() 用来阻塞当前线程
unpark(Thread thread)方法来
唤醒一个被阻塞的线
7.
Condition接口
任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、
wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现
等待/通知模式。Condition接口也提 供了类似Object的监视器方法,与Lock配合可以实现等待/通知模
式,但是这两者在使用方式及功能特性上还是有差别的
主要方法:
await():当前线程进入等待状态直到被通知(signal)或中断
signal():
唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁
接口实现ConditionObject
ConditionObject是同步器AbstractQueuedSynchronizer的内部类,
主要包括:等待队列、等待和通知
Condition使用代码示例:
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
|
package
com.tlk.chapter5;
import
java.util.concurrent.locks.Condition;
import
java.util.concurrent.locks.Lock;
import
java.util.concurrent.locks.ReentrantLock;
/**
* 有界队列,满添加await,移除signal
* @author tanlk
* @date 2017年5月17日下午7:16:53
* @param <T>
*/
public
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
InterruptedException{
lock.lock();
try
{
while
(count == items.length){
notFull.await();
}
items[addIndex] = t;
if
(++addIndex == items.length) {
addIndex =
0
;
}
++count;
notEmpty.signal();
}
finally
{
lock.unlock();
}
}
//由头部删除一个元素,如果数组为空,则删除线程进入等待状态,直到有新添加元素
@SuppressWarnings
(
"unchecked"
)
public
T remove()
throws
InterruptedException{
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();
}
}
public
int
getAddIndex() {
return
addIndex;
}
public
int
getRemoveIndex() {
return
removeIndex;
}
public
int
getCount(){
return
count;
}
}
|
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
|
package
com.tlk.chapter5;
import
com.tlk.chapter4.SleepUtils;
/**
* 有界队列测试
* @author tanlk
* @date 2017年5月17日下午7:17:52
*/
public
class
BoundedQueueTest {
static
BoundedQueue<Integer> boundedQueue =
new
BoundedQueue<Integer>(
50
);
public
static
void
main(String[] args)
throws
InterruptedException {
new
addWorker().start();
new
removeWorker().start();
}
static
class
addWorker
extends
Thread{
@Override
public
void
run() {
for
(
int
i=
0
; i<
1000
; i++){
try
{
System.out.println(
"addIndex:"
+boundedQueue.getAddIndex());
System.out.println(
"removeIndex:"
+boundedQueue.getRemoveIndex());
System.out.println(
"count:"
+boundedQueue.getCount());
boundedQueue.add(i);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
static
class
removeWorker
extends
Thread{
@Override
public
void
run() {
for
(
int
i=
0
; i<
1000
; i++){
try
{
SleepUtils.second(
1
);
System.out.println(boundedQueue.remove());
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
|