zoukankan      html  css  js  c++  java
  • 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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    【刷题】面筋-mysql-如何对数据库进行备份
    【刷题】面筋-数据库-mysql的优化
    【刷题】面筋-MySQL中char、varchar和text三者的区别
    【刷题】面筋-sql-学生成绩单里两门成绩>80的学生名字
    【刷题】面筋-网络-无效链接,死链接,错误链接
    【刷题】面筋-算法-在海量IP中找出访问次数最多的100个IP
    【刷题】面筋-shell:统计一个文件中重复的行和重复次数
    【刷题】面筋-linux-如何查找出现频率最高的100个ip地址
    【刷题】面筋-linux 如何将文件从一台服务器转移到另一台服务器
    【刷题】面筋-Linux-vi显示或关闭行号:set number
  • 原文地址:https://www.cnblogs.com/sharpest/p/10850992.html
Copyright © 2011-2022 走看看