Java高并发程序设计(九)并发调试和JDK8新特性

1.多线程调试的方法

如下代码存在安全问题,在多个线程操作时会出现数据越界异常。

package com.thread.chapter09;

import java.util.ArrayList;

/**
 * t1,t2同时对 arrayList进行操作,会出现脏读,脏写
 * 数组越界异常是因为 size在被扩容之前,比如 t1,t2都
 * 读到了size = 9;然后都认为可以继续做 add 操作,t1把
 * size++ 到长度为10,t2仍然以为size = 9,可以add,结果
 * size++ 为11,就发生了越界访问。
 * Created by chenbin on 2019\8\24 0024.
 */
public class UnSafeArrayList {
    static ArrayList al = new ArrayList();
    static class AddTask implements Runnable {
        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();

    }
}


2.线程dump及分析

以一个死锁案例来介绍查看堆栈信息,寻找死锁原因:

package com.thread.chapter09;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 死锁案例,以及死锁检测解决(中断死锁线程)
 * Created by chenbin on 2019\8\16 0016.
 */
public class DeadLockDemo2 implements Runnable {
    public int lock;
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();

    public DeadLockDemo2(int lock) {
        this.lock = lock;
    }

    public void run() {
        try {
            if (lock == 1) {
                lock1.lockInterruptibly();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
                lock2.lockInterruptibly();
            } else {
                lock2.lockInterruptibly();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
                lock1.lockInterruptibly();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
            }
            System.out.print(Thread.currentThread().getId() +
                    ":线程退出");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DeadLockDemo2 r1 = new DeadLockDemo2(1);
        DeadLockDemo2 r2 = new DeadLockDemo2(2);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();
        Thread.sleep(1000);
    }
}

这段代码运行,线程一直不会结束。用cmd打开命令窗口,jps命令查看进程:

Java高并发程序设计(九)并发调试和JDK8新特性_第1张图片

使用 jstack 3620 命令查看该进程下所有的线程状态:

Java高并发程序设计(九)并发调试和JDK8新特性_第2张图片

Java高并发程序设计(九)并发调试和JDK8新特性_第3张图片

3.JDK8对并发的新支持

3.1LongAdder
– 和AtomicInteger类似的使用方式
– 在AtomicInteger上进行了热点分离
– public void add(long x)
– public void increment()
– public void decrement()
– public long sum()
– public long longValue()
– public int intValue()
Java高并发程序设计(九)并发调试和JDK8新特性_第4张图片
3.2CompletableFuture

– 实现CompletionStage接口(40余个方法)
– Java 8中对Future的增强版,支持函数式编程。不需要自己写Callable任务。
– 支持流式调用

代码案例:

package com.thread.chapter09.jdk8;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

/**
 * jdk8对并发的新支持
 * future模式
 * Created by chenbin on 2019\8\24 0024.
 */
public class CompletableFutureDemo {
    public static Integer calc(int para) {
        try {
            //模拟一个长时间执行
            Thread.sleep(5000);
        } catch (InterruptedException e) {

        }
        return para * para;
    }

    public static void main(String[] args) {
        //1.耗时操作,单独起一个异步线程来计算
        final CompletableFuture future =
                CompletableFuture.supplyAsync(()-> calc(50));
        //2.其它操作在主线程中继续
        System.out.println("step 2.");
        System.out.println("step 3.");
        System.out.println("step 4.");
        /**
         * 3.最后获取第一步耗时操作的计算结果,若2完成后1仍未完成,则会等到1完成后
         * 返回结果
         */
        try {
            System.out.println("获取future的计算结果:" + future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

static CompletableFuture supplyAsync(Supplier supplier);
static CompletableFuture supplyAsync(Supplier supplier, Executor executor);
static CompletableFuture runAsync(Runnable runnable);
static CompletableFuture runAsync(Runnable runnable, Executor executor);
Java高并发程序设计(九)并发调试和JDK8新特性_第5张图片
3.3StampedLock

– 读写锁的改进
– 读不阻塞写

StampedLock的实现思想
– CLH自旋锁
– 锁维护一个等待线程队列,所有申请锁,但是没有成功的线程都记录在这个队列中。每一个节点(一个
节点代表一个线程),保存一个标记位(locked),用于判断当前线程是否已经释放锁。
– 当一个线程试图获得锁时,取得当前等待队列的尾部节点作为其前序节点。并使用类似如下代码判断前
序节点是否已经成功释放锁:
while (pred.locked) {
}
– 不会进行无休止的自旋,会在在若干次自旋后挂起线程

package com.thread.chapter09.jdk8;

import java.util.concurrent.locks.StampedLock;

/**
 * StampedLock
 * 乐观读
 */
public class StampLockDemo3 {

    private int balance;

    private StampedLock lock = new StampedLock();
    public StampLockDemo3() {
        balance=10;
    }

    /**
     * 读的时候不阻塞写,乐观读
     * 通过判断 时间戳 来判断是否需要重读(其它线程对变量做了写操作的情况下,时间戳会发生改变)
     */
    public void optimisticRead() {
        long stamp = lock.tryOptimisticRead();
        int c = balance;
        // 这里可能会出现了写操作,因此要进行判断
        if(!lock.validate(stamp)) {
            // 要重新读取
            System.out.println("要重新读取");
            stamp = lock.readLock();
            try{
                c = balance;
            }
            finally{
                lock.unlockRead(stamp);
            }
        }
        System.out.println("读取的值为:"+c);
    }

    /**
     * 写操作会获取写锁
     * @param value
     */
    public void write(int value) {
        long stamp = lock.writeLock();
        balance += value;
        lock.unlockWrite(stamp);
    }


    public static void main(String[] args) {
        StampLockDemo3 demo=new StampLockDemo3();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    demo.optimisticRead();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    demo.write(2);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }

}

Java高并发程序设计(九)并发调试和JDK8新特性_第6张图片

源代码可在github上下载:https://github.com/chenbin911029/mutiThread 

类文件在  com.thread.chapter09 包下。

备注:本文为JAVA高并发程序设计(葛一鸣著)读书笔记,以及自身的整理和实践。

你可能感兴趣的:(#,Java,多线程)