Java高并发程序设计学习笔记(十):并发调试和JDK8新特性
转自:https://blog.csdn.net/dataiyangu/article/details/87631574
多线程调试的方法
使用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导致报错。
线程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)只是一个示意的代码,不会无休止的自旋
简单来说就是每次执行自己的时候先看看前面的锁释放了没有,以此类推。
---------------------
作者:Leesin Dong
来源:CSDN
原文:https://blog.csdn.net/dataiyangu/article/details/87631574
版权声明:本文为博主原创文章,转载请附上博文链接!
Java高并发程序设计学习笔记(十):并发调试和JDK8新特性的更多相关文章
- 并发编程学习笔记(10)----并发工具类CyclicBarrier、Semaphore和Exchanger类的使用和原理
在jdk中,为并发编程提供了CyclicBarrier(栅栏),CountDownLatch(闭锁),Semaphore(信号量),Exchanger(数据交换)等工具类,我们在前面的学习中已经学习并 ...
- ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录
放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 4.2全线基础学习请点击[直达] 4.3及更高版本的补充学习请关注我的博客. ArcGIS API for JavaScr ...
- Java高并发程序设计学习笔记(九):锁的优化和注意事项
转自:https://blog.csdn.net/dataiyangu/article/details/87612028 锁优化的思路和方法减少锁持有时间减小锁粒度锁分离锁粗化举个栗子举个栗子锁消除虚 ...
- Java高并发程序设计学习笔记(十一):Jetty分析
转自:https://blog.csdn.net/dataiyangu/article/details/87894253 new Server()初始化线程池QueuedThreadPoolexecu ...
- Java高并发程序设计学习笔记(七):并行设计模式
转自:https://blog.csdn.net/dataiyangu/article/details/87123586 什么是设计模式架构模式设计模式代码模式(成例 Idiom)单例模式普通单例假如 ...
- Java高并发程序设计学习笔记(五):JDK并发包(各种同步控制工具的使用、并发容器及典型源码分析(Hashmap等))
转自:https://blog.csdn.net/dataiyangu/article/details/86491786#2__696 1. 各种同步控制工具的使用1.1. ReentrantLock ...
- Java高并发程序设计学习笔记(三):Java内存模型和线程安全
转自:https://blog.csdn.net/dataiyangu/article/details/86412704 原子性有序性可见性– 编译器优化– 硬件优化(如写吸收,批操作)Java虚拟机 ...
- Java高并发程序设计学习笔记(二):多线程基础
转自:https://blog.csdn.net/dataiyangu/article/details/86226835# 什么是线程?线程的基本操作线程的基本操作新建线程调用run的一种方式调用ru ...
- Java高并发程序设计学习笔记(一):并行简介以及重要概念
转自:https://blog.csdn.net/dataiyangu/article/details/86211544#_28 文章目录为什么需要并行?反对意见大势所趋几个重要的概念同步(synch ...
随机推荐
- HttpURLConnection提交数据
使用GET方式向服务器端提交数据 * 原理:把要提交的数据组拼到Url后面 * http协议规定数据长度不超过4kb,IE浏览器超过1kb就会丢弃掉后面的数据 * 缺点:数据不安全 * 优点:代码书写 ...
- Smarty模板实现隔行换样式
在网上找了好多关于隔行改变样式的文章,都不符合自己的要求,所以自己想了好多办法,终于把隔行改变样式拿下! 这是模板文件中商品分类列表 <!--{foreach from=$cat ...
- flutter 安卓再次点击返回退出应用
安卓手机点击实体或者虚拟返回键,会返回上一级,当到达最上层是,点击返回退出应用,为了防止用户连续点击返回,导致应用退出,在用户点击返回到最上层时,如果再次点击返回,第一次不退出,并提升用户再次点击退出 ...
- delphi stringgrid导出为excel
procedure TLiYQBYJL.btnBYJLTJDCClick(Sender: TObject); var ExcelApp, workbook, sheet: Variant; col, ...
- nginx的负载均衡和反向代理
本文介绍一些负载均衡和反向代理的一些基本概念,然后介绍如何基于nginx实现,包括两种安装nginx的方法:yum安装和源码安装,以及ngix该如何配置等. 什么是负载均衡? 概念 负载均衡是高可用网 ...
- python安装二进制k8s高可用 版本1.13.0
一.所有安装包.脚本.脚本说明.下载链接:https://pan.baidu.com/s/1kHaesJJuMQ5cG-O_nvljtg 提取码:kkv6 二.脚本安装说明 1.脚本说明: 本实验为三 ...
- Java泛型(6):extends和super关键字
(1) <T extends A> 因为擦除移除了类型信息,而无界的泛型参数调用的方法只等同于Object.但是我们可以限定这个泛型参数为某个类型A的子集,这样泛型参数声明的引用就可以用类 ...
- Springboot入门5-项目打包部署(转载)
前言 本文主要介绍SpringBoot的一些打包事项和项目部署以及在其中遇到一些问题的解决方案. SpringBoot打包 在SpringBoot打包这块,我们就用之前的一个web项目来进行打包.首先 ...
- socket --自己简单的理解
一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...
- TLC编程
NAND Flash可以划分为SLC.MLC和TLC SLC:单阶存储单元,读写速率快,可擦写次数高 MLC和TLC:多阶存储单元,MLC每个存储单元中存储2 bit数据,可以表示四种数据:SLC每个 ...