Concurrent 学习

Concurrent总结

线程和进程

​ 进程 —> QQ, 360

​ 线程 —> 进程中的程序, 每一个进程至少有一个线程

​ 多核CPU中进程可以并发执行

​ CUP同一时刻只能运行一个进程, 不同的线程来抢占CPU时间片

并发编程领域, 提醒性能本质就是提升硬件的利用率 —> 提升I/O和CPU的利用率

Volatile

可见性和有序性问题:

package com.cdqf.concurrent;

public class Test01 {
    public static void main(String[] args) {

        new Thread(() -> System.out.println(SingleTon.getInstance())).start();
        new Thread(() -> System.out.println(SingleTon.getInstance())).start();
    }
}

class SingleTon {

    private static volatile SingleTon singleTon;

    private SingleTon() {
    }

    public static SingleTon getInstance() {
        if (singleTon == null) {
            // 线程A 抢占到锁, B等待, 当线程A运行到33行时释放锁, 线程B拿到锁
            // 这时候可能会出现A线程还未完成第三步的情况, 线程B进入代码块后判断singleTon为空
            // 就会造成内存可见性问题
            synchronized (SingleTon.class) {
                if (singleTon == null) {
                    // 这里new SingleTong()其实分为三步
                    // 1. 堆内分配空间和地址
                    // 2. 将地址赋予对象
                    // 3. 初始化这个类
                    // 我们所期望的正确顺序为 1 3 2 但是由于JMM重排序的存在
                    // 可能会造成123的情况 这就是指令重排序(有序性)
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
}

以上可见性和有序性问题可以使用volatile解决

可见性:
	当使用了volatile的共享变量被一个线程修改了以后, volatile语句会强迫正在使用此共享变量的其他线程所拿到的共享变量失效, 然后重新读取

有序性:
	volatile指令会使用内存屏障屏蔽指令重排

原子性问题:

package com.cdqf.concurrent;

public class Test02 {

    private volatile static int i = 0;

    public static void main(String[] args) {

        for (int i1 = 0; i1 < 20; i1++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    // 我们所期望的i值最后为20000, 但结果却小于20000 造成这一结果的原因是
                    // 我们拆解i++ 这一步骤 1. 从主存中读取i值 2. i++ 3. 将值写到主存中 
                    // 但是由于volatile语句的作用, 可能在A线程执行到第二步的时候, B线程已经执行完毕了
                    // 这时候会强制要求A线程重新读取i的值, 造成i值的一次丢失
                    i++;
                }
            }).start();
            while (Thread.activeCount() > 2) {
                Thread.yield();
            }
        }
        System.out.println(i);
    }
}

解决办法:

​ 加锁:

package com.cdqf.concurrent;

public class Test02 {

    private static volatile int i = 0;

    private static final Object lock = new Object();

    public static void main(String[] args) {

        for (int j = 0; j < 20; j++) {
            new Thread(() -> {
                synchronized (lock) {
                    for (int k = 0; k < 1000; k++) {
                        i++;
                    }
                }
            }).start();
        }
        while (Thread.activeCount() > 2) Thread.yield();

        System.out.println(i);
    }
}
Synchronized锁

使用场景:

1.代码块

2.静态方法, 锁class

3.普通方法, 锁this

特点:

1.不可以被打断

2.synchronized锁的释放时机为出现异常jvm自动释放或同步代码块执行完毕释放

3.可重入锁 (可以对一个对象重复加锁)

Synchronized(对象在内存中的布局)三大模块:

对象头:

​ syn(A) :

​ Mark Word: 00空闲 01使用状态标识 标记A是否被使用

​ class metadata address: 指向class的指针

​ 每个对象都有一个Monitor对象: 管程

​ entryList: 当前准备抢占对象锁的线程集合

​ owner: 拥有当前monitor的线程

​ count: 如果锁被持有 count+1 , 锁被释放 count-1

​ waitSet: 当我们调用wait()方法时, 线程进入waitSet中等待nofity/nofityAll方法唤醒

​ 管程流程: 先读取count 和 owner 然后entryList其中某个线程继续进入

​ monitorEnter(): 如果能进入monitor就代表管程中已经有owner了, 指向同步代码块的开始位置

​ monitorExit(): monitor释放方法, 指向同步代码块的出去位置

无锁-------偏向锁(偏向某一个线程)------轻量级锁((交替执行)----重量级锁(竞争非常激烈)

实例数据:

对齐填充:

lock锁

​ API:

  • lock():获得锁,如果锁被占用则等待。

  • lockInterruptibly():获得锁,但优先响应中断。(留着)

  • tryLock():尝试获得锁,如果成功,则返回true;失败返回false。此方法不等待,立即返回。

  • tryLock(long time,TimeUnit):在上面的方法上加上时间

  • unlock():释放锁

  • Condition newCondition()

lock/unlock:

package com.cdqf.concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test03 implements Runnable {

    Lock lock = new ReentrantLock();

    private static int i;

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i1 = 0; i1 < 10000; i1++) {
                i++;
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {

        Test03 myThread = new Test03();
        for (int i1 = 0; i1 < 3; i1++) {
            new Thread(myThread).start();
        }

        while (Thread.activeCount() > 2) Thread.yield();

        System.out.println(i);
    }
}

trylock:

package com.cdqf.concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test03 implements Runnable {

    Lock lock = new ReentrantLock();

    private static int i;

    @Override
    public void run() {
        if (lock.tryLock()) {
            try {
                for (int i1 = 0; i1 < 10000; i1++) {
                    i++;
                }
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("未能拿到锁的线程:" + Thread.currentThread().getName());
        }

    }

    public static void main(String[] args) {

        Test03 myThread = new Test03();
        for (int i1 = 0; i1 < 3; i1++) {
            new Thread(myThread).start();
        }

        while (Thread.activeCount() > 2) Thread.yield();

        System.out.println(i);
    }
}

trylock重载:

package com.cdqf.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test03 implements Runnable {

    Lock lock = new ReentrantLock();

    private static int i;

    @Override
    public void run() {
        try {
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                try {
                    Thread.sleep(1000);
                    System.out.println("拿到锁的线程为:" + Thread.currentThread().getName());
                    for (int i1 = 0; i1 < 10000; i1++) {
                        i++;
                    }
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println("未能拿到锁的线程:" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Test03 myThread = new Test03();
        for (int i1 = 0; i1 < 3; i1++) {
            new Thread(myThread).start();
        }

        while (Thread.activeCount() > 2) Thread.yield();

        System.out.println(i);
    }
}

lockInterruptibly:

package cn.cdqf.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest2 implements Runnable{
    Lock lock = new ReentrantLock();

    @Override
    public void run() {

        try {
            lock.lockInterruptibly();

                for (; ; ) {
                    System.out.println(Thread.currentThread().getName() + "抢到锁");
                    //Thread.sleep(2000);
                }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(Thread.currentThread().getName()+"被打断了");
        }finally {
            //一定要保证释放锁
            lock.unlock();
        }
        // lock.lock();
    }

    public static void main(String[] args) throws InterruptedException {
        LockTest2 lockTest = new LockTest2();
       Thread thread = new Thread(lockTest);
        thread.start();
        Thread.sleep(2000);

        Thread thread1 = new Thread(lockTest);
        thread1.start();
        //如果thread1能获得锁 就继续运行 如果不能获得锁 我自己打断等待
        thread1.interrupt();
        //thread.interrupt();
    }
}
读写锁 ReadWriteLock

读锁:共享锁

写锁:互斥/排他锁

package com.cdqf.concurrent;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Test04 implements Runnable {

    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();


    @Override
    public void run() {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        //ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        readLock.lock();
        //writeLock.lock();
        for (int i = 0; i < 2; i++) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程:" + Thread.currentThread().getName() );
        }
        readLock.unlock();
        //writeLock.unlock();
    }

    public static void main(String[] args) {
        Test04 test04 = new Test04();
        new Thread(test04).start();
        new Thread(test04).start();
    }
}

Semaphore 信号流
package com.cdqf.concurrent;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

public class Test05 implements Runnable {

    // 放入了十个许可
    Semaphore semaphore = new Semaphore(10);

    AtomicInteger atomicInteger = new AtomicInteger();

    @Override
    public void run() {
        if (semaphore.tryAcquire()) {
            try {
                // 获得一个许可, 进入方法
                // semaphore.acquire();
                System.out.println("当前获得线程的个数为:" + atomicInteger.incrementAndGet());
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 释放许可
                semaphore.release();
            }
        } else {
            System.out.println("服务器正忙, 请稍后再试");
        }
    }

    public static void main(String[] args) {
        Test05 test05 = new Test05();
        for (int i = 0; i < 100; i++) {
            new Thread(test05).start();
        }
    }
}

运行结果:
当前获得线程的个数为:1
当前获得线程的个数为:3
当前获得线程的个数为:2
当前获得线程的个数为:4
当前获得线程的个数为:5
当前获得线程的个数为:6
当前获得线程的个数为:7
当前获得线程的个数为:8
当前获得线程的个数为:9
当前获得线程的个数为:10
服务器正忙, 请稍后再试
服务器正忙, 请稍后再试
服务器正忙, 请稍后再试
服务器正忙, 请稍后再试...
CountDownLatch
package com.cdqf.concurrent;


import java.util.concurrent.CountDownLatch;

public class Test06 {
    public static void main(String[] args) {

        final CountDownLatch latch = new CountDownLatch(2);

        new Thread(() -> {
            System.out.println("正在执行:" + Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
                latch.countDown();
                System.out.println("执行完毕:" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            System.out.println("正在执行:" + Thread.currentThread().getName());
            latch.countDown();
            System.out.println("执行完毕:" + Thread.currentThread().getName());
        }).start();

        try {
            latch.await();
            System.out.println("所有线程执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
CyclicBarrier
package com.cdqf.concurrent;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;

public class Test07 {
    public static void main(String[] args) {

        Writer writer = new Writer();
        for (int i = 0; i < 4; i++)
            new Thread(writer).start();
    }

    static class Writer implements Runnable {
        private AtomicInteger atomicInteger = new AtomicInteger(0);
        public static CyclicBarrier cyclicBarrier = new CyclicBarrier(4);

        @Override
        public void run() {
            System.out.println("线程" + Thread.currentThread().getName() + "正在写入数据...");
            try {
                Thread.sleep(atomicInteger.incrementAndGet() * 2000);      //以睡眠来模拟写入数据操作
                System.out.println("线程" + Thread.currentThread().getName() + "写入数据完毕,等待其他线程写入完毕");
                //会阻塞。。。直到 4 个线程都走到这儿
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
    }
}

CAS(CompareAndSwap) 与 AtomicInteger相关

当业务需求十个线程同时执行

for (int i = 0; i<1000 ;i++) {}

使用CAS会有以下三个核心数据:

V: 地址偏移量 —> 当i从0到1的时候 我们可以把偏移量看做是1

A: 我们的预期值, 从主存中拿到的i的值

B: 改变后的值, 从0到1这一步中我们可以把预期值看做是1

只有当地址偏移量未改变的情况下, 即 V = A 我们才会进行下一步操作 i ++, 否则的话CAS进行自旋(无限循环) 因为cas检测到在我修改数值之前我的地址偏移量产生了变化, 所以重新获取预期值

无限循环导致的时间开销大:

只能保证一个变量的原子性:

ABA问题: AtomicStampedReference类解决ABA问题(核心为标记)

线程池

Executors: executor顶级接口工具类

newCachedThreadPool: 无限大的可缓存线程池, 当执行第二个任务时第一个任务已经完成的情况下, 会复用第一个任务的线程而不会创建新线程

newFixedThreadPool: 定长线程池, 控制线程最大并发数, 超出的线程会在队列中等待

newScheduledThreadPool: 定长线程池, 支持定时以及周期性的执行任务

newSingleThreadExecutor: 单线程化的线程池, 保证所有任务按照指定的顺序执行

线程池核心设置: new ThreadPoolExecutor()

​ corePoolSize: 核心线程数, 表示线程池存在的最小线程数, 不会被回收

​ maximumPoolSize: 线程池最大线程数, 表示该线程池最多同时存在的线程数量

​ keepAliveTime & unit: 空闲的时间和单位, 表示超过该空闲时间的线程就会被回收

​ workQueue: 工作队列

​ threadFactory: 可以自定义创建线程, 例如给线程自定义名字等

​ handler:

​ 可以通过该参数自定义任务的拒接策略, 如果线程池中所有的线程都在运行并且工作队列也满载, 那么在此时提交任务线程池就会拒绝接受.

​ 拒绝策略:

​ CallerRunPolicy: 提交任务的线程会自己去执行任务

​ AbortPolicy: 默认的拒绝策略, throws RejectedExecutionException

​ DiscardPolicy: 丢弃任务, 并且不抛出异常

​ DiscardOldestPolicy: 丢弃最老的工作队列中的任务并把新任务添加进队列

​ 关闭方法:

​ shutdown(): 不会立即终止线程池, 会等待所有任务执行完毕, 并且不会再接受新任务

​ shutdownNow(): 立即终止线程池, 并尝试打断正在执行的任务, 清空队列, 返回尚未执行的任务

Callable

​ 线程创建的第三种方式, 存在的意义为, 因为Run方法没有参数, 没有返回值, 不能抛出异常, 所以我们需要另一种方法去监控线程中的异常状态等信息

package com.cdqf.concurrent;

import java.util.concurrent.*;

public class Test08 implements Callable<String> {
    public static void main(String[] args) {

        ExecutorService executorService = Executors.newCachedThreadPool();
        try {
            String s = executorService.submit(new Test08()).get();
            System.out.println(s);
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("抛出异常: " + e.getMessage());
        }
        executorService.shutdown();
    }

    @Override
    public String call() {
        System.out.println("callable线程方法运行中");
        // if (1 == 1) throw new RuntimeException("1=1异常");
        return "这是一个Callable返回值";
    }
}

Future
package com.cdqf.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * FutureTask类
 */

public class Test09 implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("callable线程执行中");
        return "callable返回值";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<String>(new Test09()){
            @Override
            protected void done() {
                System.out.println("callable任务执行结束");
            }
        };
        futureTask.run();
        String s = futureTask.get();
        System.out.println(s);
    }
}

CompletableFuture类

CompletableFuture 默认使用ForkJoinPool()线程池, 但是也可以自定义配置

runAsync(Runable) 无返回值
runAsycn(Runable, Executor) 无返回值
package com.cdqf.concurrent;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CompletableFuture类 runAsync方法
 */

public class Test10 {
    public static void main(String[] args) {
        // 新建线程池
        ExecutorService cDqf_ = Executors.newCachedThreadPool(r -> {
            Thread thread = new Thread(r);
            thread.setName("CDqf_");
            return thread;
        });

        // CompletableFuture runAsync方法实现
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
                    System.out.println("当前执行的线程是 : " + Thread.currentThread().getName());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 放入自定义线程池
        , cDqf_);

        // 线程执行异步任务
        voidCompletableFuture.join();
        System.out.println("异步任务执行结束");
        cDqf_.shutdown();
    }
}
supplyAsync(Supplier supplier)
supplyAsync(Supplier supplier, Executor)
thenApply(Function<?, ?> function) 跟supplyAsync使用同一个线程运行
thenApply(Function<?, ?> function, Executor)
thenApplyAsync(Function<?, ?> function)
thenApplyAsync(Function<?, ?> function, Executor)
package com.cdqf.concurrent;

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

/**
 * CompletableFuture类 supplyAsync方法 thenApply方法
 */

public class Test11 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        // 使用自定义FixedThreadPool定长线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        CompletableFuture<String> ret = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行方法的线程是 : " + Thread.currentThread().getName());
            // supplyAsync方法相比runAsync方法的特点之一是他有返回值, 而且是一个泛型约束的返回值
            return "supplyAsync方法的返回值";
            // 可以使用自定义线程池或默认线程池
            // thenApply(Function(有参数, 有返回值)) s 代表的是上一个方法的返回值
        }, executorService).thenApply((s) -> {
            System.out.println("上一个方法的返回值是:" + s);
            return s.toUpperCase();
        }).thenApplyAsync((s) -> {
            System.out.println("最终返回值为:" + s);
            return s.substring(0, 11);
        });
        // 这里使用get方法之后就会自定加载线程任务, 而不用使用join方法再将线程任务插入主线程
        System.out.println(ret.get());
        // 关闭线程池
        executorService.shutdown();
    }
}

thenAccept()
thenRun()
package com.cdqf.concurrent;

import java.util.concurrent.CompletableFuture;

/**
 * CompletableFuture类 thenAccept方法
 */

public class Test12 {
    public static void main(String[] args) {
        CompletableFuture<Void> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "线程运行中");
            return "supplyAsync返回值";
            // thenAccept会直接消耗掉类对象, 再在下方获取对象会得到空值
        }).thenAccept(System.out::println).thenRun(() -> System.out.println("thenRun方法执行"));
    }
}

thenCompose() 组装多个有关联关系的异步任务
package com.cdqf.concurrent;

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

/**
 * completableFuture类 thenCompose方法 thenCombine方法
 */
public class Test13 {

    private static CompletableFuture<String> firstStep() {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "线程任务开始");
            return "First Step";
        });
    }

    private static CompletableFuture<String> secondStep(Object s) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "线程任务开始");
            System.out.println("完成前置条件:" + s);
            return "Second step";
        });
    }
    // 将第一步和第二步组装起来
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> stringCompletableFuture = firstStep().thenCompose(Test13::secondStep);
        System.out.println(stringCompletableFuture.get());
    }
}
thenCombine() 组装两个没有参数关联的异步任务
package com.cdqf.concurrent;

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

/**
 * CompletableFuture类 thenCombine方法
 */

public class Test14 {

    private static CompletableFuture<String> first() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("first down");
        return CompletableFuture.supplyAsync(() -> "first");
    }

    private static CompletableFuture<String> second() throws InterruptedException {
        Thread.sleep(1000);
        System.out.println("second down");
        return CompletableFuture.supplyAsync(() -> "second");
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<String> stringCompletableFuture = first().thenCombine(second(), (ret1, ret2) -> {
            System.out.println("完成: " + ret1);
            System.out.println("完成: " + ret2);
            return "mission success";
        });
        System.out.println(stringCompletableFuture.get());
    }
}
allOf 等待多个异步任务执行成功
package com.cdqf.concurrent;

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

/**
 * CompletableFuture类 allOf方法
 */

public class Test15 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> first = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("first down");
        });
        CompletableFuture<Void> second = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("second down");
        });
        CompletableFuture<Void> third = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("third down");
        });

        // 以上方法是没有先后顺序的
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(first, second, third);
        // 同步所有的方法
        voidCompletableFuture.get();
        System.out.println("all down");
    }
}
anyOf() 只要有一个任务成功, 即步入下一步, 不等待其他任务
package com.cdqf.concurrent;

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

/**
 * CompletableFuture类 anyOf方法
 */

public class Test16 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Void> first =
                CompletableFuture.runAsync(() -> {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("first down");
                });

        CompletableFuture<String> second = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("second down");
            return "second key";
        });

        CompletableFuture<Void> third =
                CompletableFuture.runAsync(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("third down");
                });

        CompletableFuture<Object> objectCompletableFuture = CompletableFuture.anyOf(first, second, third);
        objectCompletableFuture.join();
        System.out.println(objectCompletableFuture.get());
    }
}
exceptionally() 异常处理方法
package com.cdqf.concurrent;

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

/**
 * ExceptionHandler
 */

public class Test17 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<String> p = CompletableFuture.supplyAsync(() -> {
            System.out.println("first");
            return "first down";
        }).thenApply((s) -> {
            System.out.println(s + " second going...");
            return "second down";
        }).thenApply((s) -> {
            System.out.println(s);
            if (1==1) throw new RuntimeException();
            return "finish";
            // 因为出现异常,  下面的代码不会运行
        }).thenApply((s) -> {
            System.out.println(s);
            return "Project done";
        });
        System.out.println(p.get());
    }

    private static void exceptionally() {
        CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {
            System.out.println("first");
            return "first";
        }).thenApply((s) -> {
            System.out.println("second " + s);
            return "second";
        }).thenApply((s) -> {
            System.out.println("finish " + s);
            if (1 == 1) throw new RuntimeException("1=1 异常");
            return "down";
        }).exceptionally((e) -> {
            // 因为使用了exceptionally方法, 所以可以捕获到异常
            return "exception";
        });
        String join = exceptionally.join();
        System.out.println("finally:" + join);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值