亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

Java AQS框架基礎

2023-08-01 12:32:51
9
0

AQS是AbstractQueuedSynchronizer類的簡稱,Java并發工具包的大部分類的實現基礎都基于這個類。AQS的核心是通過一個共享變量來同步狀態,AQS只負責:
• 線程阻塞隊列的維護
• 線程阻塞和喚醒
共享變量的修改都是通過Unsafe類提供的CAS操作來實現的,AQS的主要方法是acquire和release,是個模板方法類。
并發工具包中AQS的類關系圖

一、AQS的簡單實現

下面通過一個demo來看怎么使用AQS和Lock接口來實現一個互斥鎖:

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
public class Mutex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0,1)){
this.setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int releases) {
if(getState() == 0) throw new IllegalMonitorStateException();
this.setExclusiveOwnerThread(null);
this.setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}


Condition newCondition(){
return new ConditionObject();
}
}

private final Sync sync = new Sync();

@Override
public void lock() {
sync.acquire(1);
}

@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}

@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(0);
}

public boolean isLocked(){
return sync.isHeldExclusively();
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
public static void main(String[] args) {
final Mutex lock = new Mutex();
for(int i = 0; i < 10; i++){
new Thread(new Runnable(){
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getId() + " acquired the lock.");
lock.unlock();
}
}).start();
//簡單地讓線程讓for循環的順序阻塞在lock上
SleepUtils.second(10);
}
}

}

通過一個內部類來繼承AbstractQueuedSynchronizer類,而Lock接口中的方法主要靠Sync內部類來實現。

二、CLH lock Queue
AQS的實現是基于CLH lock Queue線程阻塞隊列的,CLH lock Queue是一個FIFO的雙向隊列。當線程獲取同步狀態失敗時,同步器會將當前的線程和等待狀態信息構造成一個Node對象并放到同步隊列中,同時會阻塞當前線程。當同步狀態釋放時,會將首節點中的線程喚醒,使其再次嘗試獲取同步狀態。
內部類Node就是CLH lock Queue的一個變種,通過自旋來等待而不是睡眠,自旋就一個循環的過程。
基于CLH lock Queue實現自旋鎖的一個例子:

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
public class ClhSpinLock {


private final ThreadLocal<Node> prev;
private final ThreadLocal<Node> node;
private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());

public ClhSpinLock(){
this.node = new ThreadLocal<Node>(){
protected Node initialValue(){
return new Node();
}
};
this.prev = new ThreadLocal<Node>(){
protected Node initialValue(){
return null;
}
};
}

public void lock(){
final Node node = this.node.get();
node.locked = true;
//一個CAS操作即可將當前線程對應的節點加到隊列中,
//并且同時獲取前繼節點的引用,然后等待前繼節點釋放鎖
Node prev = this.tail.getAndSet(node);
this.prev.set(prev);
System.out.println(Thread.currentThread().getName() + " waiting for lock");
while(prev.locked){//進入自旋
}
System.out.println(Thread.currentThread().getName() + " get the lock");
}

public void unlock(){
final Node node = this.node.get();
node.locked = false;
this.node.set(this.prev.get());
System.out.println(Thread.currentThread().getName() + " releases the lock.");
}

private static class Node{
private volatile boolean locked;
}

public static void main(String[] args) {
final ClhSpinLock lock = new ClhSpinLock();
lock.lock();
for(int i = 0; i < 10; i++){
new Thread(new Runnable(){
@Override
public void run() {
lock.lock();
SleepUtils.mills(10);
lock.unlock();
}
},"Thread"+i).start();

}
lock.unlock();
}

}

執行后輸出結果為:
main waiting for lock
main get the lock
Thread0 waiting for lock
Thread3 waiting for lock
Thread1 waiting for lock
Thread4 waiting for lock
Thread2 waiting for lock
Thread7 waiting for lock
main releases the lock.
Thread0 get the lock
Thread8 waiting for lock
Thread5 waiting for lock
Thread0 releases the lock.
Thread9 waiting for lock
Thread6 waiting for lock
Thread3 get the lock
Thread3 releases the lock.
Thread1 get the lock
Thread1 releases the lock.
Thread4 get the lock
Thread4 releases the lock.
Thread2 get the lock
Thread7 get the lock
Thread2 releases the lock.
Thread7 releases the lock.
Thread8 get the lock
Thread5 get the lock
Thread8 releases the lock.
Thread5 releases the lock.
Thread9 get the lock
Thread6 get the lock
Thread9 releases the lock.
Thread6 releases the lock.

而AQS的線程不單純是自旋,還包括了反復地休眠和喚醒的過程,AQS的等待隊列的后繼是通過前繼節點喚醒的。AQS結合了自旋和休眠/喚醒的優點。

三、LockSupport

除了LockSupport,Java中的Object對象就有wait/notify來實現線程的阻塞和喚醒。兩者之間的區別主要是面向的對象不同。wait/notify是Object對象固有的方法,與對象有關。而LockSupport是與線程相關的,更符合線程語義。wait/notify被喚醒的線程是被動的,要準確控制哪個線程阻塞、喚醒很困難。
在使用wait之前需要獲取對象的監視器,而線程喚醒后執行也需要獲取對象的監控才行執行。LockSupport為需要獲取監視器。LockSupport機制是每次unpark給線程一個許可,而park相反,如果當前線程有許可,則消耗一個許可并返回。否則會阻塞線程直到線程重新獲取許可。這個wait/notify的機制是不一樣的。

四、處理中斷

? Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。
? LockSupport也能響應中斷,但不會拋出InterruptedException,它依賴中斷狀態(Interrupted status),如果線程被中斷退出阻塞,該值設置為true。通過線程的interrupted/isInterrupted可能獲取該值,兩個方法的區別是interrupted獲取后會清理,也就是將Interrupted status設置為false。
處理InterruptedException的原則有兩種:
? • 繼續設置Interrupted status
? • 拋出新的InterruptedException

0條評論
0 / 1000
chuoo
13文章數
0粉絲數
chuoo
13 文章 | 0 粉絲
原創

Java AQS框架基礎

2023-08-01 12:32:51
9
0

AQS是AbstractQueuedSynchronizer類的簡稱,Java并發工具包的大部分類的實現基礎都基于這個類。AQS的核心是通過一個共享變量來同步狀態,AQS只負責:
• 線程阻塞隊列的維護
• 線程阻塞和喚醒
共享變量的修改都是通過Unsafe類提供的CAS操作來實現的,AQS的主要方法是acquire和release,是個模板方法類。
并發工具包中AQS的類關系圖

一、AQS的簡單實現

下面通過一個demo來看怎么使用AQS和Lock接口來實現一個互斥鎖:

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
public class Mutex implements Lock {
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(0,1)){
this.setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int releases) {
if(getState() == 0) throw new IllegalMonitorStateException();
this.setExclusiveOwnerThread(null);
this.setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}


Condition newCondition(){
return new ConditionObject();
}
}

private final Sync sync = new Sync();

@Override
public void lock() {
sync.acquire(1);
}

@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}

@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(0);
}

public boolean isLocked(){
return sync.isHeldExclusively();
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
public static void main(String[] args) {
final Mutex lock = new Mutex();
for(int i = 0; i < 10; i++){
new Thread(new Runnable(){
@Override
public void run() {
lock.lock();
System.out.println(Thread.currentThread().getId() + " acquired the lock.");
lock.unlock();
}
}).start();
//簡單地讓線程讓for循環的順序阻塞在lock上
SleepUtils.second(10);
}
}

}

通過一個內部類來繼承AbstractQueuedSynchronizer類,而Lock接口中的方法主要靠Sync內部類來實現。

二、CLH lock Queue
AQS的實現是基于CLH lock Queue線程阻塞隊列的,CLH lock Queue是一個FIFO的雙向隊列。當線程獲取同步狀態失敗時,同步器會將當前的線程和等待狀態信息構造成一個Node對象并放到同步隊列中,同時會阻塞當前線程。當同步狀態釋放時,會將首節點中的線程喚醒,使其再次嘗試獲取同步狀態。
內部類Node就是CLH lock Queue的一個變種,通過自旋來等待而不是睡眠,自旋就一個循環的過程。
基于CLH lock Queue實現自旋鎖的一個例子:

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
public class ClhSpinLock {


private final ThreadLocal<Node> prev;
private final ThreadLocal<Node> node;
private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());

public ClhSpinLock(){
this.node = new ThreadLocal<Node>(){
protected Node initialValue(){
return new Node();
}
};
this.prev = new ThreadLocal<Node>(){
protected Node initialValue(){
return null;
}
};
}

public void lock(){
final Node node = this.node.get();
node.locked = true;
//一個CAS操作即可將當前線程對應的節點加到隊列中,
//并且同時獲取前繼節點的引用,然后等待前繼節點釋放鎖
Node prev = this.tail.getAndSet(node);
this.prev.set(prev);
System.out.println(Thread.currentThread().getName() + " waiting for lock");
while(prev.locked){//進入自旋
}
System.out.println(Thread.currentThread().getName() + " get the lock");
}

public void unlock(){
final Node node = this.node.get();
node.locked = false;
this.node.set(this.prev.get());
System.out.println(Thread.currentThread().getName() + " releases the lock.");
}

private static class Node{
private volatile boolean locked;
}

public static void main(String[] args) {
final ClhSpinLock lock = new ClhSpinLock();
lock.lock();
for(int i = 0; i < 10; i++){
new Thread(new Runnable(){
@Override
public void run() {
lock.lock();
SleepUtils.mills(10);
lock.unlock();
}
},"Thread"+i).start();

}
lock.unlock();
}

}

執行后輸出結果為:
main waiting for lock
main get the lock
Thread0 waiting for lock
Thread3 waiting for lock
Thread1 waiting for lock
Thread4 waiting for lock
Thread2 waiting for lock
Thread7 waiting for lock
main releases the lock.
Thread0 get the lock
Thread8 waiting for lock
Thread5 waiting for lock
Thread0 releases the lock.
Thread9 waiting for lock
Thread6 waiting for lock
Thread3 get the lock
Thread3 releases the lock.
Thread1 get the lock
Thread1 releases the lock.
Thread4 get the lock
Thread4 releases the lock.
Thread2 get the lock
Thread7 get the lock
Thread2 releases the lock.
Thread7 releases the lock.
Thread8 get the lock
Thread5 get the lock
Thread8 releases the lock.
Thread5 releases the lock.
Thread9 get the lock
Thread6 get the lock
Thread9 releases the lock.
Thread6 releases the lock.

而AQS的線程不單純是自旋,還包括了反復地休眠和喚醒的過程,AQS的等待隊列的后繼是通過前繼節點喚醒的。AQS結合了自旋和休眠/喚醒的優點。

三、LockSupport

除了LockSupport,Java中的Object對象就有wait/notify來實現線程的阻塞和喚醒。兩者之間的區別主要是面向的對象不同。wait/notify是Object對象固有的方法,與對象有關。而LockSupport是與線程相關的,更符合線程語義。wait/notify被喚醒的線程是被動的,要準確控制哪個線程阻塞、喚醒很困難。
在使用wait之前需要獲取對象的監視器,而線程喚醒后執行也需要獲取對象的監控才行執行。LockSupport為需要獲取監視器。LockSupport機制是每次unpark給線程一個許可,而park相反,如果當前線程有許可,則消耗一個許可并返回。否則會阻塞線程直到線程重新獲取許可。這個wait/notify的機制是不一樣的。

四、處理中斷

? Thread.interrupt()方法不會中斷一個正在運行的線程。這一方法實際上完成的是,在線程受到阻塞時拋出一個中斷信號,這樣線程就得以退出阻塞的狀態。更確切的說,如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個中斷異常(InterruptedException),從而提早地終結被阻塞狀態。
? LockSupport也能響應中斷,但不會拋出InterruptedException,它依賴中斷狀態(Interrupted status),如果線程被中斷退出阻塞,該值設置為true。通過線程的interrupted/isInterrupted可能獲取該值,兩個方法的區別是interrupted獲取后會清理,也就是將Interrupted status設置為false。
處理InterruptedException的原則有兩種:
? • 繼續設置Interrupted status
? • 拋出新的InterruptedException

文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0