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 ...
随机推荐
- 在业务控制方法中写入模型变量收集参数,且使用@InitBind来解决字符串转日期类型
1) 在默认情况下,springmvc不能将String类型转成java.util.Date类型,所有我们只能在Action 中自定义类型转换器 <form action="${pa ...
- java错误与异常
java异常处理机制 异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常, 让程序尽最大可能恢复正常并继续执行,且保持代码的清晰.Java中的异常可以是函数中的语句执 ...
- R语言常用包简介
- What happens when you type an URL in the browser and press enter?
What happens when you type an URL in the browser and press enter? 1. You type maps.google.com into t ...
- Centos7 FastDFS 搭建
安装libfastcommon 首先第一步是安装libfastcommon,我这里将libfastcommon上传到的/opt目录下,直接解压: yum -y install gcc-c++ yum ...
- dfs -cogs 5 P服务点设置
题目链接:http://cogs.pro:8081/cogs/problem/problem.php?pid=FSXJmiJSg 问题描述为了进一步普及九年义务教育,政府要在某乡镇建立P所希望小学 ...
- 不要轻易使用ffmpeg的audio_device_number来设置音频设备
最近项目中需要使用ffmpeg实现录音功能,使用的ffmpeg-3.4.4的库,根据源代码dshow.c中的定义 { "audio_device_number", "se ...
- windows下安装Sonar
1.sonar安装: sonar有三部分组成: 1.服务端:显示分析结果和sonar相关配置 2.客户端:对项目运行源代码进行运算和分析 3.数据库:存储sonar配置和代码分析结果的数据库 2.so ...
- Python之Web前端Ajax
Ajax: 对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上. 1.传统的Web应用 一个简单操 ...
- 查找担保圈-step5-比较各组之间的成员,对组的包含性进行查询,具体见程序的注释-版本2
USE [test] GO /****** Object: StoredProcedure [dbo].[p03_get_groupno_e2] Script Date: 2019/7/8 15:01 ...