Java高併發程序設計學習筆記(十):併發調試和JDK8新特性

轉自:https://blog.csdn.net/dataiyangu/article/details/87631574java

多線程調試的方法
使用Eclipse進行多線程調試
線程dump及分析
分析死鎖案例
代碼
jstack調試
jps命令找到當前這個java的進程號
運行jstack命令
JDK8對併發的新支持
LongAdder
CompletableFuture
基本
異步執行
工廠方法:
流式調用
組合多個CompletableFuture
StampedLock
StampedLock的實現思想
多線程調試的方法
使用Eclipse進行多線程調試
看以下一段代碼:數組

public class UnsafeArrayList {
static ArrayList al=new ArrayList();
static class AddTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
for(int i=0;i<1000000;i++)
al.add(new Object());
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new AddTask(),"t1");
Thread t2=new Thread(new AddTask(),"t2");
t1.start();
t2.start();
Thread t3=new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
},"t3");
t3.start();
}
}

ArrayList不是線程安全的。
安全

把斷點打到ArrayList的add方法處,發現仍是在classLoader層面上的,並無到達咱們的應用層的實現。多線程

上面的條件斷點只有當不是主線程的時候纔會生效,經過上面的程序不難看出,整個應用層面和主線程並無太大的關係,主要和線程t1 t2有關係併發

 

經過打斷點的方式復現問題發現異步

public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
1
2
3
4
5
是在ensureCapacityInternal(size + 1);這行出現了問題,t1中size變成了9,t2size++,這個時候t1並不知情,致使size不一致,·8致使報錯。ide

線程dump及分析
jstack 3992 能夠導出當前虛擬機全部運行的線程。
在%JAVA_HOME%/bin目錄下面(jstack 3992 )函數

分析死鎖案例
代碼
代碼簡介:東西南北四個小車造成的死鎖高併發

import java.util.concurrent.locks.ReentrantLock;性能

public class DeadLock extends Thread {
protected Object myDirect;
static ReentrantLock south = new ReentrantLock();
static ReentrantLock north = new ReentrantLock();
static ReentrantLock west = new ReentrantLock();
static ReentrantLock east = new ReentrantLock();

public DeadLock(Object obj){
this.myDirect = obj;
if (myDirect == south) {
this.setName("south");
}
if (myDirect == north) {
this.setName("north");
}
if (myDirect == west) {
this.setName("west");
}
if (myDirect == east) {
this.setName("east");
}

}

@Override
public void run() {
if (myDirect == south) {
try {
west.lockInterruptibly();
Thread.sleep(500);
south.lockInterruptibly();
System.out.println("car to south has passed");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("car to south is killed");
}finally {
if (west.isHeldByCurrentThread())
west.unlock();
if (south.isHeldByCurrentThread())
south.unlock();
}
}
if (myDirect == north) {
try {
east.lockInterruptibly();
Thread.sleep(500);
north.lockInterruptibly();
System.out.println("car to south has passed");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("car to south is killed");
}finally {
if (east.isHeldByCurrentThread())
east.unlock();
if (north.isHeldByCurrentThread())
north.unlock();
}
}
if (myDirect == west) {
try {
north.lockInterruptibly();
Thread.sleep(500);
west.lockInterruptibly();
System.out.println("car to south has passed");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("car to south is killed");
}finally {
if (north.isHeldByCurrentThread())
north.unlock();
if (west.isHeldByCurrentThread())
west.unlock();
}
}
if (myDirect == east) {
try {
south.lockInterruptibly();
Thread.sleep(500);
east.lockInterruptibly();
System.out.println("car to south has passed");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("car to south is killed");
}finally {
if (south.isHeldByCurrentThread())
south.unlock();
if (east.isHeldByCurrentThread())
east.unlock();
}
}
}

public static void main(String[] args) throws InterruptedException {
DeadLock car2South = new DeadLock(south);
DeadLock car2North = new DeadLock(north);
DeadLock car2West = new DeadLock(west);
DeadLock car2East = new DeadLock(east);
car2South.start();
car2East.start();
car2North.start();
car2West.start();
Thread.sleep(1000);

}
}

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
107
108
運行結果:


1
什麼也沒有輸出,程序還在不斷的運行着。

jstack調試
jps命令找到當前這個java的進程號
➜ ~ jps
1682 Launcher
1714 Jps
1683 DeadLock
1397 RemoteMavenServer
1370
1
2
3
4
5
6
運行jstack命令
jstack 1683
1
jstack -h
1
發現-l參數能夠看到更多的參數

jstack -l 1683
1
結果:

"west" #12 prio=5 os_prio=31 tid=0x00007ff6cc062000 nid=0x3d03 waiting on condition [0x0000700006ab7000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000079578bda8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at DeadLock.run(DeadLock.java:65)

Locked ownable synchronizers:
- <0x000000079578bd78> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"north" #11 prio=5 os_prio=31 tid=0x00007ff6cd843800 nid=0x3f03 waiting on condition [0x00007000069b4000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000079578bd78> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at DeadLock.run(DeadLock.java:49)

Locked ownable synchronizers:
- <0x000000079578bdd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"east" #13 prio=5 os_prio=31 tid=0x00007ff6cd843000 nid=0x4103 waiting on condition [0x00007000068b1000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000079578bdd8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at DeadLock.run(DeadLock.java:81)

Locked ownable synchronizers:
- <0x000000079578bd48> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"south" #10 prio=5 os_prio=31 tid=0x00007ff6cd842000 nid=0x3b03 waiting on condition [0x00007000067ae000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000079578bd48> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:897)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at DeadLock.run(DeadLock.java:33)

Locked ownable synchronizers:
- <0x000000079578bda8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
- Found one Java-level deadlock:
=============================
"west":
waiting for ownable synchronizer 0x000000079578bda8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "south"
"south":
waiting for ownable synchronizer 0x000000079578bd48, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "east"
"east":
waiting for ownable synchronizer 0x000000079578bdd8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "north"
"north":
waiting for ownable synchronizer 0x000000079578bd78, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "west"

Java stack information for the threads listed above:
===================================================
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
能夠看到east中有這句話:parking to wait for <0x000000079578bdd8>
south中Locked ownable synchronizers:
- <0x000000079578bda8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
能夠知道east在等待0x000000079578bdd8,而0x000000079578bdd8是被south持有的。以此類推。

一樣
「west」:
waiting for ownable synchronizer 0x000000079578bda8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by 「south」
也是能看出具體的緣由。

末尾更清楚:

=============================
"west":
waiting for ownable synchronizer 0x000000079578bda8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "south"
"south":
waiting for ownable synchronizer 0x000000079578bd48, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "east"
"east":
waiting for ownable synchronizer 0x000000079578bdd8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "north"
"north":
waiting for ownable synchronizer 0x000000079578bd78, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
which is held by "west"

Java stack information for the threads listed above:
===================================================
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
JDK8對併發的新支持
LongAdder
– 和AtomicInteger相似的使用方式
– 在AtomicInteger上進行了熱點分離
– public void add(long x)
– public void increment()增長一
– public void decrement()減一
– public long sum() 由於是分離成16份,這裏是一個求和的操做
– public long longValue() 同上
– public int intValue() Long轉化成整形
性能比AtomicLong高不少,由於LongAdder是相似於HashMao的熱點分離。
示意:

cas更新
線程一-------->cell1 |
線程二-------->cell2 |---sum---->
線程三-------->cell3 |----求和---> value
線程四-------->cell4 |
1
2
3
4
5
基本思想:
如上,當高併發的時候,將一個數分解成多個cell,線程一訪問cell1,線程二訪問cell2,以此類推,從而減小衝突的機率,可是當併發的線程數極少的時候,將數分紅數組,則會消耗很大的性能,起到相反的做用,因此Longadd自己是有優化的,自己經過base數據(相似於AtomicLong),當發現一次衝突的時候就分紅兩個,在發現一次衝突分紅四個,以此類推。

CompletableFuture
基本
– 實現CompletionStage接口(40餘個方法)
– Java 8中對Future的加強版
– 支持流式調用

stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() ->
System.out.println())
1
2
完成後獲得通知

public static class AskThread implements Runnable {
CompletableFuture <Integer> re = null;
public AskThread(CompletableFuture <Integer> re) {
this.re = re
}
@Override
public void run() [
int myRe = 0;
try {
//返回future值的平方
myRe = re.get) * re.get();
} catch (Exception e) {
System.out.println(myRe);
}
public static void main(String[] args) throws InterruptedException {
final CompletableFuture <Integer> future = new CompletableFuture<>();
//將future傳到線程中
new Thread(new AskThread(future)).start();
//模擬長時間的計算過程
Thread.sleep(1000);
//告知完成結果
future.complete(60);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
跟前面的future模式不一樣的是,前面的future模式完成是系統本身完成的,這裏的完成是可以開發者本身定義的,如上面的代碼future.complete(60);

異步執行
public static Integer calc(Integer para) {
try {
// 模擬一個長時間的執行
Thread.sleep(1000);
} catch (InterruptedException e) {
}
return para*para;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
final CompletableFuture<Integer> future =
//supplyAsync工廠方法,可以獲得一個CompletableFuture的實例,
//並非經過new出來的,內部會幫咱們建立一個,可以直接獲得一個實例,而後調動calc
//calc中的執行相似上面的代碼,return平法。
CompletableFuture.supplyAsync(() -> calc(50));
System.out.println(future.get());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
工廠方法:
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor); static CompletableFuture<Void> runAsync(Runnable runnable);
static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);
1
2
3
Executor executor就是線程池,supplyAsync和runAsync的區別是supplyAsync是有返回值的,runAsync就是一個單純Runnable接口,沒有返回值。

流式調用
public static Integer calc(Integer para) {
try {
// 模擬一個長時間的執行
Thread.sleep(1000);
} catch (InterruptedException e) {
}
return para*para;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Void> fu=CompletableFuture.supplyAsync(() -> calc(50))
.thenApply((i)->Integer.toString(i)) .thenApply((str)->"\""+str+"\"") .thenAccept(System.out::println);
fu.get();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
calc返回平方操做,Integer.toString轉化成String,thenApply((str)->"""+str+""") 在String兩邊加引號,thenAccept(System.out::println)輸出結果。fu.get(); 看看獲得結果了沒有。

組合多個CompletableFuture
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
1
public static Integer calc(Integer para) {
return para/2;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Void> fu =
CompletableFuture.supplyAsync(() -> calc(50)) .thenCompose((i)->CompletableFuture.supplyAsync(() -> calc(i))) .thenApply((str)->"\"" + str + "\"").thenAccept(System.out::println);
fu.get();
}
1
2
3
4
5
6
7
8
thenCompose除以四,就是五十先除以四,再除以四。
結果

"12"
1
StampedLock
– 讀寫鎖的改進
– 讀不阻塞寫
讀的時候發生了寫,不該該不讓寫操做,而應該重讀。
由於:
當讀太多的時候,可能出現寫不進去的現象,寫飢餓。
stemp時間戳

public class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
//tryOptimisticRead樂觀讀,即上面提到的思想
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
//驗證stemp,若是讀的過程當中,進行了寫操做,返回零或者其餘的數,拒絕操做
//若是在讀x的過程當中修改了y,看到上面的move函數,對sl加鎖解鎖,每次的stemp值都是不同的
//和這裏的對比
if (!sl.validate(stamp)) {
//若是不支持樂觀讀,就用最原始的讀寫鎖的方法。
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
}
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
StampedLock的實現思想
– CLH自旋鎖
– 鎖維護一個等待線程隊列,全部申請鎖,可是沒有成功的線程都記錄在這個隊列中。每個節點(一個 節點表明一個線程),保存一個標記位(locked),用於判斷當前線程是否已經釋放鎖。
– 當一個線程試圖得到鎖時,取得當前等待隊列的尾部節點做爲其前序節點。並使用相似以下代碼判斷前 序節點是否已經成功釋放鎖:
示意代碼:

while (pred.locked) { }
1
StampedLock的實現思想
– 不會進行無休止的自旋,會在在若干次自旋後掛起線程
上面(while)只是一個示意的代碼,不會無休止的自旋

簡單來講就是每次執行本身的時候先看看前面的鎖釋放了沒有,以此類推。

相關文章
相關標籤/搜索