Java高并发(线程创建以及线程池,异步调用,CompletableFuture)

news2024/11/16 9:44:47

线程

是进程中的一个实体,线程本身是不会独立存在的。
进程是代码在数据集合上的一次运行活动 是系统进行资源分配调度的基本单位 。
线程则是进程的一个执行路径, 一个进程中至少有 线程,进程中多个线程共享进程的资源。
操作系统在分配资源时是把资源分配给进程的, 但是 CPU 资源 它是被分配到线程的,因为真正要占用CPU资源运行的是线程 所以也说线程是 CPU 分配的基本单位。

创建线程

/**
	创建线程方式一:继承Thread
		优点:在run方法中使用this就可以获取当前线程,无需Thread.currentThread()
		缺点:不支持多继承,继承了Thread,不能继承其他类
*/
public class ThreadTest{
	public static class MyThread extends Thread{
		@Override
		public void run(){
			System.out.println("I am a child thread");
		}
	}

	public static void main(String[] args){
		MyThread thread = new MyThread();
		thread.start();
	}
}
/**
	创建线程方式二:实现Runnable接口
		优点:,两个线程共用task代码逻辑,如果需要,可以 RunableTask添加参数进行任务区分。
		缺点:两种方式都没有返回值
*/
public static class RunableTase implements Runnable{
	@Override
	public void run(){
		System.out.println("I am a child thread");
	}
}

public static void main(String[] args) throws InterruptedException{
	RunableTask task = new RunableTask();
	new Thread(task).start();
	new Thread(task).start();
}
/**
	创建线程方式三:实现Callable接口
*/
public static class CallerTase implements Callable<String>{
	@Override
	public String call() throws Excetion{
		return "Hello";
	}
}

public static void main(String[] args) throws InterruptedException{
	//创建异步任务
	FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
	//启动线程
	new Thread(futureTask).start();
	try{
		String result = futureTask.get();
		System.out.println();
	}catch(Exception e){
		e.printStackTrace();
	}
}

=====================================================================

Runnable无返回值

public static void main(String[] args){
	
	Runnable hellos = () -> {
		for(int i = 1; i <= 1000; i+){
			System.out.println("Hello " + i);
		}
	};
	Runnable goodbyes = () -> {
		for(int i = 1; i <= 1000; i+){
			System.out.println("Goodbye " + i);
		}
	};
	
	Executor executor = Executors.newCachedThreadPool();
	executor.execute(hellos);
	executor.execute(goodbyes); //结果是交叉输出
}

Callable有返回值
Future接口代表计算的结果,有如下方法

  • V get() 阻塞,知道有了可用结果或者超时,返回计算值。
  • V get(long timeout,TimeUnit.unit)
  • boolean cancel(boolean mayInterruptIfRunning) 取消任务。如果任务还未运行,就不会将任务列入运行计划。否则如果参数为true,则正在运行任务的线程会被中断
  • boolean isCancelled()
  • boolean isDone()
//Executor的子接口ExecutorService
ExecutorService exec = Executors.newFixedThreadPool();

Callable<V> task = () -> {
	while(){
		if(Thread.currentThread().isInterrupted()){
			return null;
		}
	}
	return result;
}

线程池
在这里插入图片描述

/**
	线程池的创建:
	corePoolSize(线程池的基本大小)当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程
	
	maximumPoolSize(线程池最大数量)。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如
果使用了无界的任务队列这个参数就没什么效果。

	keepAliveTime(线程活动保持时间)线程池的工作线程空闲后,保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率
	TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)
	
	runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。
		ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序
		LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列
		SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列
		PriorityBlockingQueue:一个具有优先级的无限阻塞队列
		
	RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。
		AbortPolicy:直接抛出异常
		CallerRunsPolicy:只用调用者所在线程来运行任务
		DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
		DiscardPolicy:不处理,丢弃掉

*/
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,milliseconds,runnableTaskQueue, handler);
/**
	向线程池提交任务:
		execute()提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功
		submit()用于提交需要返回值的任务线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完
*/
threadsPool.execute(new Runnable() {
	@Override
	public void run() {
		// TODO Auto-generated method stub
	}
});

Future<Object> future = executor.submit(harReturnValuetask);
try {
	Object s = future.get();
} catch (InterruptedException e) {
	// 处理中断异常
} catch (ExecutionException e) {
	// 处理无法执行任务异常
} finally {
	// 关闭线程池(的shutdown或shutdownNow)
	executor.shutdown();
}

异步调用vs同步调用(Future)

同步调用

@Component
public class Task{
	
	public static Random random = new Random();

	public void doTaskOne() throws Exception{
		
		System.out.println("开始做任务一");
		long start = System.currentTimeMillis();
		Thread.sleep(random.nextInt(10000));
		long end = System.currentTimeMillis();
		System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");
	}

	public void doTaskTwo() throws Exception {
        System.out.println("开始做任务二");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务二,耗时:" + (end - start) + "毫秒");
    }

    public void doTaskThree() throws Exception {
        System.out.println("开始做任务三");
        long start = System.currentTimeMillis();
        Thread.sleep(random.nextInt(10000));
        long end = System.currentTimeMillis();
        System.out.println("完成任务三,耗时:" + (end - start) + "毫秒");
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
public class ApplicationTests{
	
	@Autowired
	private Task task;

	@Test
	public void test() throws Exception{
		task.doTaskOne();
		task.doTaskTwo();
		task.doTaskThree();
	}
}

在这里插入图片描述

异步调用

指程序在顺序执行时,不等待异步调用的语句返回结果就执行后面的程序。

SpringBoot中通过注解@Async
注:@Async所修饰的函数不要定义为static类型,这样异步调用不会生效

@Component
public class Task{
	
	@Async
	public void doTaskOne() throws Exception{

	}

	@Async
    public void doTaskTwo() throws Exception {
        // 同上内容,省略
    }

    @Async
    public void doTaskThree() throws Exception {
        // 同上内容,省略
    }
}

SpringBoot启动类@EnableAsync

@SpringBootApplication
@EnableAsync
public class Application{
	
	public static void main(){
		 SpringApplication.run(Application.class, args);
	}
}

继续反复执行单元测试,遇到各种不同的结果:

  • 没有任何任务相关的输出
  • 有部分任务相关的输出
  • 乱序的任务相关的输出

原因是目前doTaskOne、doTaskTwo、doTaskThree三个函数的时候已经是异步执行了。主程序在异步调用之后,主程序并不会理会这三个函数是否执行完成了,由于没有其他需要执行的内容,所以程序就自动结束了,导致了不完整或是没有输出任务相关内容的情况

为了让doTaskOne、doTaskTwo、doTaskThree能正常结束,假设我们需要统计一下三个任务并发执行共耗时多少,这就需要等到上述三个函数都完成调动之后记录时间,并计算结果。
那么我们如何判断上述三个异步调用是否已经执行完成呢?我们需要使用Future来返回异步调用的结果,就像如下方式改造doTaskOne函数:

@Async
public Future<String> doTaskOne()throws Exception{
	
	System.out.println("开始做任务一");
    long start = System.currentTimeMillis();
    Thread.sleep(random.nextInt(10000));
    long end = System.currentTimeMillis();
    System.out.println("完成任务一,耗时:" + (end - start) + "毫秒");

	return new AsyncResult<>("任务一完成");
}

按照如上方式改造一下其他两个异步函数之后,下面我们改造一下测试用例,让测试在等待完成三个异步调用之后来做一些其他事情。

@Test
public void test() throws Exception {

    long start = System.currentTimeMillis();

    Future<String> task1 = task.doTaskOne();
    Future<String> task2 = task.doTaskTwo();
    Future<String> task3 = task.doTaskThree();

    while(true) {
        if(task1.isDone() && task2.isDone() && task3.isDone()) {
            // 三个任务都调用完成,退出循环等待
            break;
        }
        Thread.sleep(1000);
    }

    long end = System.currentTimeMillis();

    System.out.println("任务全部完成,总耗时:" + (end - start) + "毫秒");

}

执行一下上述的单元测试,可以看到如下结果:

开始做任务一
开始做任务二
开始做任务三
完成任务三,耗时:37毫秒
完成任务二,耗时:3661毫秒
完成任务一,耗时:7149毫秒
任务全部完成,总耗时:8025毫秒

CompletableFuture

在这里插入图片描述

Future回顾

使用Future.get()的方式阻塞调用线程,或者使用轮询方式判断 Future.isDone 任务是否结束,再获取结果

@Test
public void testFuture() throws ExecutionException,InterruptedException{
	
	ExecutorService.executorService = Executors.newFixedThreadPool(5);
	Future<String> future = executorService.submit(() -> {
		Thread.sleep(2000);
		return "hello";
	});

	//Future.get()的方式阻塞调用线程,直到5个线程都执行结束
	System.out.println(future.get());
	System.out.println("end");
}

Future无法解决多个异步任务需要相互依赖的场景,简单点说就是,主线程需要等待子线程任务执行完毕之后在进行执行,想到了CountDownLatch,代码如下

/**
	定义两个Future,第一个通过用户id获取用户信息,第二个通过商品id获取商品信息
*/
@Test
public void testCountDownLatch() throws InterruptedExcetion,ExecutionException{
	
	ExecutorService executorService = Executors.newFixedThreadPool(5);
	CountDownLatch downLatch = new CountDownLatch(2); //门阀初始化为2

	long startTime = System.currentTimeMillis();
	Future<String> userFuture = executorService.submit(() -> {
		
		Thread.sleep(500);//模拟查询商品耗时500毫秒
		downLatch.countDown(); //初始化减一
		return "用户A";
	});

	Future<String> goodsFuture = executorService.submit(() -> {
		
		Thread.sleep();//模拟查询商品耗时400毫秒
		downLatch.countDown();
		return "商品A";
	});

	downLatch.await();

	//模拟主程序耗时时间
	Thread.sleep(600);
	System.out.println("获取用户信息:" + userFuture.get());
	System.out.println("获取商品用户信息:" + goodsFuture.get());
	System.out.println("总共用时:" + (System.currentTimeMilis() - startTime) + "ms");
}

在这里插入图片描述
不用异步操作,执行时间应该是:500+400+600 = 1500,用异步操作后实际只用1110。

CompletableFuture

@Test
public void testCompletableInfo() throws InterruptedExcption,ExecutionException{
	
	//调用用户服务获取用户基本信息
	CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() -> {
		
		try{
			Thread.sleep(500);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		return "用户A";
	});

	//调用商品服务获取商品用户基本信息
	CompletableFuture<String> goodsFuture = CompletableFuture.supplyAsync(() -> {
		
		try{
			Thread.sleep(400);
		}catch(InterruptedException e){
			e.printStackTrance();
		}

		return "商品A";
	});
	
	System.out.println("获取用户信息:" + userFuture.get());
	System.out.println("获取商品信息:" + goodsFuture.get());

	//模拟主程序耗时时间
	Thread.sleep(600);
	System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
}

在这里插入图片描述
通过CompletableFuture可以很轻松的实现CountDownLatch的功能。

四种创建CompletableFuture的方式

/**
	CompletableFuture四个静态方法执行异步任务
		supplyAsync 执行任务,支持返回值
		runAsync 执行任务,没有返回值
*/

//使用默认内置线程池ForkJoinPool.commonPool()  根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}  
//自定义线程,根据supplier构建任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}  

//使用默认内置线程forkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable){..}  
//自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor){..}  

四种获取CompltableFuture结果的方式

//方式一  Future中已经提供
public T get()  
//方式二  提供了超时处理,如果在指定时间未获取结果将抛出超时异常
public T get(long timeout, TimeUnit unit)  
//方式三  立即获取结果不阻塞,结果计算已完成将返回结果获计算过程中的异常,如果未计算完成将返回设定的valueIfAbsent值
public T getNow(T valueIfAbsent)   
//方式四  方法里不会抛出异常
public T join()  
@Test
public void testComplatableGet() throws InterruptedException,ExecutionException{

	CompetableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
		try{
			Thread.sleep(1000);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		return "商品A";
	});

	//getNow方法测试
	System.out.println(cp1.getNow("商品B"));//第一个执行结果为 「商品B」,因为要先睡上1秒结果不能立即获取

	//join方法测试
	CompletableFuture<Ineger> cp2 = CompletableFuture.supplyAsync((() -> 1/0));
	System.out.println(cp2.join()); //join方法获取结果方法里不会抛异常,但是执行结果会抛异常,抛出的异常为CompletionException
	System.out.println("-----------------------------------------------------");  

	//get测试
	ComplatableFuture<Integer> cp3 = CompletableFuture.supplyAsync((() -> 1/0));
	System.out.println(cp3.get());//get方法获取结果方法里将抛出异常,执行结果抛出的异常为ExecutionException
}

异步回调方法

在这里插入图片描述

1、thenRun/thenRunAsync

做完第一个任务后,再做第二个任务,第二个任务也没有返回值

@Test
public void testCompletableThenRunAsync() throws InterruptedException,ExecutionException{
	
	long startTime = System.currentTimeMillis();
	CompletableFuture<Void> cp1 = CompletableFuture.runAsync(() -> {
		
		try{
			//执行任务A
			Thread.sleep(600);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	});

	CompletableFuture<Void> cp2 = cp1.thenRun(() -> {
		
		try {  
            //执行任务B  
            Thread.sleep(400);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
	});
	
	//get方法测试
	System.out.println(cp2.get());

	 //模拟主程序耗时时间  
    Thread.sleep(600);  

	// 总共用时1610ms
    System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");  
}

thenRun/thenRunAsync区别

如果执行了第一个任务的时候,传入了一个自定义线程池:

  • 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务公用同一个线程池
  • 调用thenRunAsync执行第二个任务时,则第一个任务使用的是自己传入的线程池,第二个任务使用的是ForkJoin线程池。
    说明: 后面介绍的thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是这个。

2、thenAccept/thenAcceptAsync

第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果作为入参传递到回调方法中,回调方法没有返回值。

@Test
public void testCompletableThenAccept() throws ExecutionException,InterruptedException{
	
	 long startTime = System.currentTimeMillis();
	 CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
		return "dev";
	});

	CompletableFuture<Void> cp2 = cp1.thenAccept((a) -> {
		System.out.println("上一个任务的返回结果为:" + a);
	});

	cp2.get();
}

3、 thenApply/thenApplyAsync

表示第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的.

@Test
public void testCompletableThenApply() throws ExecutionException,InterruptedException{
	
	CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
		return "dev";
	}).thenApply((a) -> {
		if(Objects.equals(a,"dev")){
			return "dev";
		}
		return "prod";
	});

	System.out.println("当前环境为:" + c1.get());
}

4、异常回调whenComplete

当CompletableFuture的任务不论是正常完成还是出现异常它都会调用 「whenComplete」这回调函数。

  • 正常完成:whenCompleta返回结果和上级任务一致,异常为null
  • 出现异常:whenCompleta返回结果为null,异常为上级任务的异常

即调用get()时,正常完成时就获取到结果,出现异常时就会抛出异常,需要你处理该异常。

@Test
public void testCompletableWhenComplete() throws ExecutionException,InterruptedException{
	
	CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {
		
		if(Math.random() < 0.5){
			throw new RuntimeException("出错了");
		}
		System.out.println("正常结束");
		return 0.11;
	}).whenComplete((aDouble,throwable) -> {
		
		if(aDouble == null){
			System.out.println("whenCompleta aDouble is null");
		}        } else {  
            System.out.println("whenComplete aDouble is " + aDouble);  
        }  
        if (throwable == null) {  
            System.out.println("whenComplete throwable is null");  
        } else {  
            System.out.println("whenComplete throwable is " + throwable.getMessage());  
        }  

	});
}

在这里插入图片描述

whenComplete+exceptionally

当出现异常时,exceptionally中会捕获该异常,给出默认返回值0.0。

@Test  
public void testWhenCompleteExceptionally() throws ExecutionException, InterruptedException {
	
	CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {  
        if (Math.random() < 0.5) {  
            throw new RuntimeException("出错了");  
        }  
        System.out.println("正常结束");  
        return 0.11;  

    }).whenComplete((aDouble, throwable) -> {  
        if (aDouble == null) {  
            System.out.println("whenComplete aDouble is null");  
        } else {  
            System.out.println("whenComplete aDouble is " + aDouble);  
        }  
        if (throwable == null) {  
            System.out.println("whenComplete throwable is null");  
        } else {  
            System.out.println("whenComplete throwable is " + throwable.getMessage());  
        }  
    }).exceptionally((throwable) -> {  
        System.out.println("exceptionally中异常:" + throwable.getMessage());  
        return 0.0;  
	});
}

在这里插入图片描述

多任务组合回调

在这里插入图片描述

1、AND组合关系

runAfterBoth、thenAcceptBoth、thenCombine:表示当任务一和任务二都完成在执行任务三。

其区别:

  • reunAfterBoth 不会把执行结果当作方法入参,且没有返回值
  • thenAcceptBoth 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • thenCombine 会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
@Test  
public void testCompletableThenCombine() throws ExecutionException, InterruptedException {
	
	//创建线程池
	ExecutorService executorService = Executors.newFixedThreadPool(10);

	//开启异步任务1
	CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
		
		System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());
		int result = 1 + 1;

		System.out.println("异步任务1结束");
		return result;
	},exectorService);
	
	//开启异步任务2
	CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {
	
		System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
        int result = 1 + 1;  
        System.out.println("异步任务2结束");  
        return result; 
	},executorService);

	//任务组合
	CompletableFuture<Integer> task3 = task.thenCombineAsync(task2,(f1,f2) -> {
		
		System.out.println("执行任务3,当前线程是:" + Thread.currentThread().getId());  
        System.out.println("任务1返回值:" + f1);  
        System.out.println("任务2返回值:" + f2);  
        return f1 + f2; 
	},executorService);

	Integer res = task3.get();  
    System.out.println("最终结果:" + res); 
}

在这里插入图片描述

2、OR组合关系

runAfterEither、acceptEither、applyToEither 都表示两个任务只要有一个任务完成就执行任务三

  • runAfterEither 不会把执行结果当作方法入参,且没有返回值
  • acceptEither 会将已经完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • applyToEither 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
@Test  
public void testCompletableEitherAsync() {  
	
	//创建线程池
	ExecutorService executorService = Executors.newFixedThreadPool(10);

	//开启异步任务1
	CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
		
		 System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());  

        int result = 1 + 1;  
        System.out.println("异步任务1结束");  
        return result;  
	},executorService);

	//开启异步任务2  
    CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
        System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
        int result = 1 + 2;  
        try {  
            Thread.sleep(3000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("异步任务2结束");  
        return result;  
    }, executorService);

	//任务组合
	task.acceptEitherAsync(task2,(res)->{
		System.out.println("执行任务3,当前线程是:" + Thread.currentThread().getId());  
        System.out.println("上一个任务的结果为:"+res);  
	},executorService);
}

在这里插入图片描述

如果把上面的核心线程数改为1也就是

ExecutorService executorService = Executors.newFixedThreadPool(1);  

运行结果就是下面的了,会发现根本没有执行任务3,显然是任务3直接被丢弃了。
在这里插入图片描述

3、多任务组合

「allOf」:等待所有任务完成
「anyOf」:只要有一个任务完成

@Test  
public void testCompletableAallOf() throws ExecutionException, InterruptedException {  

	//创建线程
	ExecutorService executorService = Executors.newFixedThreadPool(10);
	
	//开启异步任务1
	CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
		 System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());  
        int result = 1 + 1;  
        System.out.println("异步任务1结束");  
        return result;  
	},ececutorService);

	 //开启异步任务2  
    CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
        System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());  
        int result = 1 + 2;  
        try {  
            Thread.sleep(3000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("异步任务2结束");  
        return result;  
    }, executorService); 

	//开启异步任务3  
    CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {  
        System.out.println("异步任务3,当前线程是:" + Thread.currentThread().getId());  
        int result = 1 + 3;  
        try {  
            Thread.sleep(4000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("异步任务3结束");  
        return result;  
    }, executorService);

	//任务组合
	CompletableFuture<Void> allOf = CompletableFuture.allOf(task,task2,task3);

	//等待所有任务完成
	allOf.get();

	//获取所有任务的返回结果
	System.out.println("task结果为:" + task.get());  
    System.out.println("task2结果为:" + task2.get());  
    System.out.println("task3结果为:" + task3.get());  
}
@Test  
public void testCompletableAnyOf() throws ExecutionException, InterruptedException {  
    //创建线程池  
    ExecutorService executorService = Executors.newFixedThreadPool(10);  
    //开启异步任务1  
    CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {  
        int result = 1 + 1;  
        return result;  
    }, executorService);  

    //开启异步任务2  
    CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {  
        int result = 1 + 2;  
        return result;  
    }, executorService);  

    //开启异步任务3  
    CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {  
        int result = 1 + 3;  
        return result;  
    }, executorService);  

    //任务组合  
    CompletableFuture<Object> anyOf = CompletableFuture.anyOf(task, task2, task3);  
    //只要有一个有任务完成  
    Object o = anyOf.get();  
    System.out.println("完成的任务的结果:" + o);  
}  

在这里插入图片描述
1、Future需要获取返回值,才能获取异常信息

@Test  
public void testWhenCompleteExceptionally() {  
    CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {  
        if (1 == 1) {  
            throw new RuntimeException("出错了");  
        }  
        return 0.11;  
    });  

    //如果不加 get()方法这一行,看不到异常信息  
    //future.get();  
}  

Future需要获取返回值,才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。

2、CompletableFuture的get()方法是阻塞的

CompletableFuture的get()方法是阻塞的,如果使用它来获取异步调用的返回值,需要添加超时时间。

//反例  
 CompletableFuture.get();  
//正例  
CompletableFuture.get(5, TimeUnit.SECONDS);  

3、不建议使用默认线程池

CompletableFuture代码中又使用了默认的 「ForkJoin线程池」,处理的线程个数是电脑 「CPU核数-1」。在大量请求过来的时候,处理逻辑复杂的话,响应会很慢。一般建议使用自定义线程池,优化线程池配置参数。

4、自定义线程池时,注意饱和策略

CompletableFuture的get()方法是阻塞的,我们一般建议使用future.get(5, TimeUnit.SECONDS)。并且一般建议使用自定义线程池。
但是如果线程池拒绝策略是DiscardPolicy或者DiscardOldestPolicy,当线程池饱和时,会直接丢弃任务,不会抛弃异常。因此建议,CompletableFuture线程池策略最好使用AbortPolicy,然后耗时的异步线程,做好线程池隔离。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/985357.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

工单提交管理H5小程序开发

工单提交管理H5小程序开发 工单系统&#xff0c;支持多工程师分配、知识库、工单评价、自定义字段、工单通知&#xff08;短信&邮件&#xff09;、自定义产品或服务等功能。提供全部无加密服务端源码和前端源代码&#xff0c;独立部署&#xff0c;不受限制。 功能&#…

【C++】类的封装 ② ( 封装最基本的表层概念 | 类对象作为参数传递的几种情况 )

文章目录 一、类的封装 : 将数据和方法封装到一个类中1、封装最基本的表层概念2、代码分析 - 基本封装3、代码分析 - 类对象作为参数传递的几种情况 ( 指针 / 引用 / 直接 )4、完整代码示例 一、类的封装 : 将数据和方法封装到一个类中 1、封装最基本的表层概念 将数据和方法封…

concrt140.dll怎么下载,concrt140.dll修复工具(修复精灵下载)一键修复问题

今天&#xff0c;我将为大家介绍一个非常常见的问题&#xff1a;由于找不到concrt140.dll,无法继续执行代码怎么办。这个问题可能会让很多网友感到头疼&#xff0c;但是别担心&#xff0c;我会为大家提供5种最全详细的恢复方法。在接下来我将详细介绍这些问题及其解决方法。希望…

索尼 toio™ 应用创意开发征文互动小企鹅

文章目录 背景介绍产品应用动手实践环境安装工具安装更新 setuptools 到最新版本安装 toio.py安装 bleak安装 ipykernel检查是否安装成功 定义电子小企鹅的功能和逻辑小企鹅移动小企鹅脸变色小企鹅尖叫综合实现 总结 背景介绍 索尼推出的toio™ 是一款创新的游戏玩具&#xff…

改进深度学习网络的几个思路

由于要优化网络&#xff0c;老师给提供的几个思路&#xff1a; 个人学习后的几个认知&#xff1a; 1.联级特征融合模块 主要用于残差网络最后的残差块融合上 其中 R5 是经过 Res5 通过 33 的卷积获得的特征图&#xff0c;该特征图保持空间分辨率不变&#xff0c;并将通道的数…

dnmp运行时404报错

dnmp运行时404报错 问题截图&#xff1a; dnmp简介 M1芯片&#xff08;Arm CPU&#xff09; 环境中搭建PHPNGINXMYSQL的利器&#xff0c;docker容器管理当前使用的软件&#xff0c;可以简单安装软件和扩展。 localhost.conf 原始文件如下&#xff1a; server {listen 8…

21.5 CSS 网页布局方式

网页布局方式 网页布局方式: 是指浏览器对网页中的元素进行排版的方法.常见的网页布局方式包括: * 1. 标准流(文档流/普通流)布局: 这是浏览器默认的排版方式.元素按照其在文档中的位置依次排列, 可以使用CSS的盒模型属性进行水平和垂直布局.* 2. 浮动布局: 通过设置元素的fl…

uniapp项目实践总结(十二)封装通用请求上传以及下载方法

导语&#xff1a;在日常开发过程中&#xff0c;前端经常要和后端进行接口联调&#xff0c;获取并且渲染数据到页面中&#xff0c;接下来就总结一下 uniapp 中获取请求、文件下载和上传的一些方法。 目录 原理分析方法实现实战演练案例展示 原理分析 主要是使用uni.request方…

基于YOLOV8模型的海上船只目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOV8模型的海上船只目标检测系统用于日常生活中检测与定位海上船只目标&#xff08;散装货船&#xff08;bulk cargo carrier&#xff09;、集装箱船&#xff08;container ship&#xff09;、渔船&#xff08;fishing boat&#xff09;、普通货船&#…

【嵌入式开发 Linux 常用命令系列 7.1 -- awk 过滤列中含有特定字符的行】

文章目录 awk 过滤列中字符串 上篇文章:嵌入式开发 Linux 常用命令系列 7 – awk 常用方法详细介绍 awk 过滤列中字符串 cat test.log | awk -F $31 {print $0}说明&#xff1a; -F 以什么分隔列&#xff0c;这里是以空格为分隔符&#xff1b;$3代表第3列&#xff1b;$3…

生存游戏手游推荐,适合长期玩的生存类手游

今天小编为大家带来了生存游戏手游推荐&#xff0c;适合长期玩的生存类手游。许多朋友现在喜欢冒险&#xff0c;想体验荒野生活&#xff0c;但在现实中&#xff0c;由于各种原因可能实现不了。游戏中的生存可以满足玩家对狂野生存的幻想&#xff0c;让现实中未实现的梦想在虚拟…

MyBatis-Plus学习笔记总结

一、查询 构造器分为QueryWrapper和LambdaQueryWrapper 创建实体类User package com.system.mybatisplus.model;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.…

华为云云服务器评测|在云耀云服务器L实例上部署battle-city坦克大战小游戏

华为云云服务器评测&#xff5c;在云耀云服务器L实例上部署battle-city坦克大战小游戏 一、前言1.1 云耀云服务器L实例简介1.2 battle-city坦克大战小游戏简介 二、本次实践介绍2.1 本次实践简介2.2 本次环境规划 三、购买云耀云服务器L实例3.1 登录华为云3.2 购买云耀云服务器…

八种十倍提升API性能的方式

提起API&#xff0c;作为程序员来说并不陌生&#xff0c;很多程序员的大部分工作都是围绕着它&#xff0c; 然而&#xff0c;有些内容被大家忽略&#xff0c;API的性能会直接影响产品的用户体验&#xff0c;比如&#xff0c;一个视频软件&#xff0c;播放1s后需要加载5s&#x…

Android 状态栏显示运营商名称

Android 原生设计中在锁屏界面会显示运营商名称&#xff0c;用户界面中&#xff0c;大概是基于 icon 数量长度显示考虑&#xff0c;对运营商名称不作显示。但是国内基本都加上运营商名称。对图标显示长度优化基本都是&#xff1a;缩小运营商字体、限制字数长度、信号图标压缩上…

SAM论文翻译

文章目录 Abstract1、Introduction2、Related Work3、Methodology3.1、Semantic Graph3.2、Semantic Aware Module3.3、Decoder3.4、Loss Function 4、Experiments4.1、Datasets4.2、Implementation Details4.3、Evaluation Protocol4.4、Comparison with State-of-the-Art 论文…

SpringBoot粗浅分析

应用分析 1、依赖管理机制 在springBoot项目中&#xff0c;导入starter-web所有想换依赖都会被导入&#xff0c;甚至不用去规定它们的版本号。它是根据Maven的依赖传递原则来设置&#xff0c;只需要导入场景启动器&#xff0c;场景启动器自动把这个场景的所有核心依赖全部导入…

对极几何与三角化求3D空间坐标

一&#xff0c;使用对极几何约束求R,T 第一步&#xff1a;特征匹配。提取出有效的匹配点 void find_feature_matches(const Mat &img_1, const Mat &img_2,std::vector<KeyPoint> &keypoints_1,std::vector<KeyPoint> &keypoints_2,std::vector&l…

管理类联考——数学——汇总篇——知识点突破——数据分析——计数原理——减法原理除法原理

减法原理 正面难则反着做(“ − - −”号) 【思路】当出现“至少、至多”、“否定用语"等正面较难分类的题目&#xff0c;可以采用反面进行求解&#xff0c;注意部分反面的技巧以及“且、或"的反面用法。 除法原理 看到相同&#xff0c;定序用除法消序( “ &quo…

JavaScript中点号运算符与方括号运算符

这篇文章将介绍如何在对象中获取数据、修改数据。在JavaScript中&#xff0c;点号运算符和方括号运算符都可以用于访问对象的属性。 我们还是使用上节课的代码来演示 const ITshareArray { firstname: “张三”, secondname: “二愣子”, age: 2033-1997, job: “程序员”, fr…