Java multithreading 11: pool de thread assíncrono CompletableFuture

Introdução

Para tornar o programa mais eficiente e permitir que a CPU trabalhe com a máxima eficiência, usaremos a programação assíncrona. A primeira coisa que vem à mente é começar um novo thread para fazer um determinado trabalho. Indo um passo adiante, para que o novo encadeamento retorne um valor para dizer ao encadeamento principal que as coisas estão feitas, o Futuro está quase em cena. No entanto, o método fornecido por Future é que o encadeamento principal consulte ativamente o novo encadeamento. Se houver uma função de retorno de chamada, seria legal. Portanto, para satisfazer alguns dos arrependimentos do futuro, o poderoso CompletableFuture veio junto com o Java 8.

Futuro

O multi-threading tradicional torna o programa mais eficiente, afinal, ele é assíncrono e permite que a CPU funcione totalmente, mas isso é limitado a threads recém-abertos sem que seu thread principal tenha que se preocupar com isso. Por exemplo, você inicia um novo thread apenas para calcular 1 + ... + ne, em seguida, imprime o resultado. Às vezes, você precisa do thread filho para retornar o resultado do cálculo e, para realizar cálculos adicionais no thread principal, você precisa de um Future.

Veja o exemplo a seguir, o thread principal calcula 2 + 4 + 6 + 8 + 10; o thread filho calcula 1 + 3 + 5 + 7 + 9; finalmente, as duas partes do resultado precisam ser adicionadas ao thread principal .

public class OddNumber implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(3000);
        int result = 1 + 3 + 5 + 7 + 9;
        return result;
    }
}
public class FutureTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        OddNumber oddNumber = new OddNumber();
        Future<Integer> future = executor.submit(oddNumber);
        long startTime = System.currentTimeMillis();
        int evenNumber = 2 + 4 + 6 + 8 + 10;
        try {
            Thread.sleep(1000);
            System.out.println("0.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
            int oddNumberResult = future.get();//这时间会被阻塞
            System.out.println("1+2+...+9+10="+(evenNumber+oddNumberResult));
            System.out.println("1.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
输出结果:
0.开始了:1001秒
1+2+...+9+10=55
1.开始了:3002秒

Dê uma olhada na interface Future , apenas cinco métodos são relativamente simples

//取消任务,如果已经完成或者已经取消,就返回失败
boolean cancel(boolean mayInterruptIfRunning);
//查看任务是否取消
boolean isCancelled();
//查看任务是否完成
boolean isDone();
//刚才用到了,查看结果,任务未完成就一直阻塞
V get() throws InterruptedException, ExecutionException;
//同上,但是加了一个过期时间,防止长时间阻塞,主线程也做不了事情
V get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException;

CompletableFuture

As cinco maneiras de ver o futuro acima não são muito ricas. Como nosso thread principal é chamado de principal, ele deve ser eu como o thread principal. Espero que o thread filho tome a iniciativa de me notificar quando estiver concluído. Para isso, o Java 8 trouxe CompletableFuture, uma classe de implementação do Future. Na verdade, a parte mais fascinante de CompletableFuture não é enriquecer muito as funções de Future, mas combinar perfeitamente os novos recursos de fluxos Java8.

Realize o retorno de chamada, operação de acompanhamento automática

Deixe-me falar sobre o método (um dos) que CompletableFuture implementa callback com antecedência: thenAccept ()

    public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
        return uniAcceptStage(null, action);
    }

O parâmetro possui um Consumidor, que utiliza os novos recursos do Java 8 e parametrização de comportamento, ou seja, o parâmetro não é necessariamente um tipo ou classe básica, mas também pode ser uma função (comportamento) ou um método (interface).

public class OddNumberPlus implements Supplier<Integer> {
    @Override
    public Integer get() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1+3+5+7+9;
    }
}
public class CompletableFutureTest {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        final int evenNumber = 2 + 4 + 6 + 8 + 10;
        CompletableFuture<Integer> oddNumber = CompletableFuture.supplyAsync(new OddNumberPlus());
        try {
            Thread.sleep(1000);
            System.out.println("0.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
            //看这里,实现回调
            oddNumber.thenAccept(oddNumberResult->
                        {
                            System.out.println("1.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
                            System.out.println("此时计算结果为:"+(evenNumber+oddNumberResult));
                        });
            oddNumber.get();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
输出结果:
0.开始了:1006秒
1.开始了:3006秒
此时计算结果为:55

Vale a pena mencionar que o pool de conexão de tarefa não é mostrado neste exemplo, e o programa selecionará um pool de conexão de tarefa ForkJoinPool.commonPool () por padrão.

    private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

ForkJoinPool foi iniciado no JDK7 e é chamado de estrutura de ramificação / mesclagem. Uma tarefa pode ser dividida recursivamente em muitas tarefas moleculares para formar diferentes fluxos para execução paralela , junto com poderosos algoritmos de roubo de trabalho . Melhore muito a eficiência. Obviamente, você também pode especificar o pool de conexão.

CompletableFuture merge

Java 8 enriquece a implementação de Future.CompletableFuture tem muitos métodos para todos usarem, mas a partir do exemplo acima, na verdade, CompletableFuture pode executar funções que se parecem com Future. Afinal, seu CompletableFuture não é bloqueado quando você usa o método get (). My Future pode obter o valor de retorno sozinho e realizar algumas operações manualmente (embora o método principal deva ser muito desconfortável). Então, a próxima coisa, Futuro será muito problemático de se fazer. Supondo que nosso método principal execute apenas a operação de coleções ímpares mais coleções pares, a operação de calcular essas duas coleções com antecedência é transferida de forma assíncrona para duas threads filhas. Isso mesmo, inicie dois threads, e quando ambos os threads forem concluídos, executamos a adição final. O problema é, como você sabe qual thread filho terminou por último? (Parece que você pode fazer uma votação, chamar o método isDone () indefinidamente ...) A função CompletableFuture rica nos fornece um método para esperar que os dois threads filhos terminem e, em seguida, realizar a operação de adição:

    //asyncPool就是上面提到的默认线程池ForkJoinPool
    public <U,V> CompletableFuture<V> thenCombineAsync(
        CompletionStage<? extends U> other,
        BiFunction<? super T,? super U,? extends V> fn) {
        return biApplyStage(asyncPool, other, fn);
    }

Veja um exemplo:

public class OddCombine implements Supplier<Integer> {
    @Override
    public Integer get() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1+3+5+7+9;
    }
}
public class EvenCombine implements Supplier<Integer> {
    @Override
    public Integer get() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 2+4+6+8+10;
    }
}

public class CompletableCombineTest {
    public static void main(String[] args) throws Exception{
        CompletableFuture<Integer> oddNumber = CompletableFuture.supplyAsync(new OddCombine());
        CompletableFuture<Integer> evenNumber = CompletableFuture.supplyAsync(new EvenCombine());
        long startTime = System.currentTimeMillis();
        CompletableFuture<Integer> resultFuturn = oddNumber.thenCombine(evenNumber,(odd,even)->{
            return odd + even;
        });
        System.out.println(resultFuturn.get());
        System.out.println("0.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
    }
}
输出结果:
55
0.开始了:3000秒

Aqui, simulamos um dormindo por 1 segundo e outro dormindo por 3 segundos, mas o tempo real de solicitação da rede é incerto. O mais legal não é o fenômeno, mas a operação acima usou o conceito de fluxo Java8.

Dois threads filhos não são suficientes, portanto, há uma função ** anyOff () **, que pode suportar vários CompletableFutures e aguardará a conclusão de todas as tarefas.

    public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
        return andTree(cfs, 0, cfs.length - 1);
    }

É muito parecido, existe um método que termina quando termina a primeira execução e as tarefas subsequentes não esperam mais, o que pode ser considerado uma condição suficiente.

    public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
        return orTree(cfs, 0, cfs.length - 1);
    }

Com base no exemplo acima, aumente um pouco mais o tempo da classe OddNumberPlus:

public class OddNumberPlus implements Supplier<Integer> {
    @Override
    public Integer get() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1+3+5+7+9;
    }
}
public class CompletableCombineTest {
    public static void main(String[] args) throws Exception{
        CompletableFuture<Integer> oddNumber = CompletableFuture.supplyAsync(new OddCombine());
        CompletableFuture<Integer> evenNumber = CompletableFuture.supplyAsync(new EvenCombine());
        CompletableFuture<Integer> testNumber = CompletableFuture.supplyAsync(new OddNumberPlus());
        long startTime = System.currentTimeMillis();
        CompletableFuture<Object> resultFuturn = CompletableFuture.anyOf(oddNumber,evenNumber,testNumber);
        System.out.println(resultFuturn.get());
        System.out.println("0.开始了:"+ (System.currentTimeMillis()-startTime) +"秒");
    }
}
输出结果:
30
0.开始了:1000秒

Combate real: estatísticas assíncronas nos dados do relatório da página inicial

/**
	 * 统计首页报表数据
	 * zfq
	 */
	@RequiresPermissions("udef:defWorksheet:view")
	@ResponseBody
	@RequestMapping(value = "homeReport")
	public String  findAjaxNum( HttpServletRequest request) {
		Map<String,Object> resultMap=new HashMap<>();
		User user = UserUtils.getUser();
		List<Role> roleList = user.getRoleList();
		Role role=new Role();
		role.setId("");
		roleList.contains(role);
		String formRequest = request.getParameter("homeReportParameter");
		String dsf = defWorksheetService.wkstDataScopeFilter(user.getCurrentUser(), "off", "u");
 		CompletableFuture<List<DefWorksheet>> todayAllNumFuture=null;//今日运维境况
		CompletableFuture<Integer> yearAllNumFuture=null;	//全年工单总量
		CompletableFuture<Integer> yearFinishNumFuture=null;//全年工单完成量
		CompletableFuture<List<DefWorksheet>> groupByMonthFuture=null;//月工单量统计
		CompletableFuture<List<DefWorksheet>> groupByTypeFuture=null;//工单分类统计
		CompletableFuture<List<DefWorksheet>> groupByMonthDayFuture=null;//工单分类统计

		/*
		Integer yearFinishNum=0;
		Integer yearAllNum=0;
		List<DefWorksheet> groupByTypeWorksheetList=new ArrayList<>();
		List<DefWorksheet> groupByMonthDayWorksheetList=new ArrayList<>();
		List<DefWorksheet> groupByMonthWorksheetList=new ArrayList<>();
		List<DefWorksheet> todayAllNumWorksheetList=new ArrayList<>();
		List<CompletableFuture> futureList=new ArrayList<>();*/

		if(formRequest.contains("todayNum")) {
			DefWorksheet defWorksheet=new DefWorksheet();
			defWorksheet.getSqlMap().put("dsf",dsf);
			defWorksheet.setBeginCreateDate(DateUtils.getDayStartTime(new Date()));
			defWorksheet.setEndCreateDate(new Date());
			defWorksheet.setWorksheetState(null);
			todayAllNumFuture = CompletableFuture.supplyAsync(() -> defWorksheetService.findWorksheetStateCount(defWorksheet));
			//futureList.add(todayAllNumFuture);
		}
		if(formRequest.contains("yearAllNum")){
			DefWorksheet defWorksheet=new DefWorksheet();
			defWorksheet.getSqlMap().put("dsf",dsf);
			defWorksheet.setBeginCreateDate(DateUtils.getCurrYearFirst());
			defWorksheet.setEndCreateDate(new Date());
			defWorksheet.setWorksheetState(null);//全年运维工单总量
			yearAllNumFuture = CompletableFuture.supplyAsync(() -> defWorksheetService.findYearCount(defWorksheet));
			//futureList.add(yearAllNumFuture);
			DefWorksheet defFinishWorksheet=new DefWorksheet();
			defFinishWorksheet.getSqlMap().put("dsf",dsf);
			defFinishWorksheet.setBeginCreateDate(DateUtils.getCurrYearFirst());
			defFinishWorksheet.setEndCreateDate(new Date());
			defFinishWorksheet.setWorksheetState(null);//全年运维工单总量
			defFinishWorksheet.setWorksheetState(8);//全年工单完成总量
			yearFinishNumFuture = CompletableFuture.supplyAsync(() -> defWorksheetService.findYearCount(defFinishWorksheet));
			//futureList.add(yearFinishNumFuture);
		}
		if(formRequest.contains("groupByMonthNum")){
			DefWorksheet defWorksheet=new DefWorksheet();
			defWorksheet.getSqlMap().put("dsf",dsf);
			defWorksheet.setBeginCreateDate(DateUtils.getBeforeMonthDate(new Date(),12));
			defWorksheet.setEndCreateDate(new Date());
			defWorksheet.setWorksheetState(null);//全年运维工单总量
			groupByMonthFuture = CompletableFuture.supplyAsync(() -> defWorksheetService.groupByMonthNum(defWorksheet));
			//futureList.add(groupByMonthFuture);
		}
		if(formRequest.contains("groupByWorksheetType")){
			DefWorksheet defWorksheet=new DefWorksheet();
			defWorksheet.getSqlMap().put("dsf",dsf);
			defWorksheet.setBeginCreateDate(DateUtils.getmindate());
			defWorksheet.setEndCreateDate(new Date());
			defWorksheet.setWorksheetState(null);//本月工单分类统计
			groupByTypeFuture = CompletableFuture.supplyAsync(() -> defWorksheetService.groupByWorksheetType(defWorksheet));
			//futureList.add(groupByTypeFuture);
		}
		if(formRequest.contains("groupByMonthDayNum")){
			DefWorksheet defWorksheet=new DefWorksheet();
			defWorksheet.getSqlMap().put("dsf",dsf);
			defWorksheet.setBeginCreateDate(DateUtils.getmindate());
			defWorksheet.setEndCreateDate(new Date());
			defWorksheet.setWorksheetState(null);//全年运维工单总量
			groupByMonthDayFuture= CompletableFuture.supplyAsync(() -> defWorksheetService.groupByMonthDayNum(defWorksheet));
			//futureList.add(groupByMonthDayFuture);
		}
		//CompletableFuture[] allFutureTask = new CompletableFuture[futureList.size()];
		//futureList.toArray(allFutureTask);

		/* if(todayAllNumFuture!=null&&yearAllNumFuture!=null &&yearFinishNumFuture!=null
				&&groupByMonthFuture!=null&& groupByTypeFuture!=null&& groupByMonthDayFuture!=null){*/
		//CompletableFuture.allOf(todayAllNumFuture,yearAllNumFuture,yearFinishNumFuture,groupByMonthFuture,groupByTypeFuture,groupByMonthDayFuture).join();
		//}
		//CompletableFuture.allOf(allFutureTask);

		//CompletableFuture.allOf(yearAllNumFuture).join();

		if(formRequest.contains("todayNum")){
			List<DefWorksheet>  todayAllNumWorksheetList = todayAllNumFuture.join();
			AtomicReference<Integer> todayRegisterNum= new AtomicReference<>(0);
			AtomicReference<Integer> todayHandleNum= new AtomicReference<>(0);
			AtomicReference<Integer>  todayFinishNum=new AtomicReference<>(0);
			AtomicReference<Integer>  todayTobeClaimedNum=new AtomicReference<>(0);
			if(todayAllNumWorksheetList.size()>0){
				resultMap.put("todayAllNum",todayAllNumWorksheetList.size());
				todayAllNumWorksheetList.stream().forEach(defWorksheetD ->{
					if(defWorksheetD.getWorksheetState()==-1){
						todayRegisterNum.updateAndGet(v -> v + 1);
					}else if(defWorksheetD.getWorksheetState()==3){
						todayHandleNum.updateAndGet(v -> v + 1);
					}else if(defWorksheetD.getWorksheetState()==8){
						todayFinishNum.updateAndGet(v -> v + 1);
					}else if(defWorksheetD.getWorksheetState()==2){
						todayTobeClaimedNum.updateAndGet(v -> v + 1);
					}
				});
			}
			resultMap.put("todayRegisterNum",todayRegisterNum);
			resultMap.put("todayHandleNum",todayHandleNum);
			resultMap.put("todayFinishNum",todayFinishNum);
			resultMap.put("todayTobeClaimedNum",todayTobeClaimedNum);
		}
		if(formRequest.contains("yearAllNum")){
			Integer yearAllNum=yearAllNumFuture.join();
			resultMap.put("yearAllNum",yearAllNum);
			Integer yearFinishNum =yearFinishNumFuture.join();
			resultMap.put("yearFinishNum",yearFinishNum);
		}
		if(formRequest.contains("groupByMonthNum")){
			List<DefWorksheet> 	groupByMonthWorksheetList=groupByMonthFuture.join();
			List<Object> resultList=new ArrayList<>();
			List<String>  months=new ArrayList<>();
			List<Integer> integers=new ArrayList<>();
			groupByMonthWorksheetList.stream().forEach(defWorksheetD ->{
				months.add(defWorksheetD.getGroupDate());
				integers.add(defWorksheetD.getCountValue());
			});

			resultList.add(months);
			resultList.add(integers);
			resultMap.put("groupByMonthNum",resultList);
		}
		if(formRequest.contains("groupByWorksheetType")){
			List<Object> resultList=new ArrayList<>();
			List<DefWorksheet> groupByTypeWorksheetList= groupByTypeFuture.join();
			groupByTypeWorksheetList.stream().forEach(defWorksheetD ->{
				Map<String,Object> mapData=new HashMap<>();
				String name="";
				if(StringUtils.isBlank(defWorksheetD.getQsType1())){
					name="未选择";
				}else{
					name=BasicTreeUtils.getBasicTreeLabel(defWorksheetD.getQsType1(),"sys_basicTree_gong_dan_fen_lei","");
				}
				mapData.put("name",name);
				mapData.put("value",defWorksheetD.getCountValue());
				resultList.add(mapData);
			});
			resultMap.put("groupByWorksheetType",resultList);
		}
		if(formRequest.contains("groupByMonthDayNum")){
			List<DefWorksheet> 	groupByMonthDayWorksheetList=groupByMonthDayFuture.join();
			List<Object> resultList=new ArrayList<>();
			List<String>  months=new ArrayList<>();
			List<Integer> integers=new ArrayList<>();
			groupByMonthDayWorksheetList.stream().forEach(defWorksheetD ->{
				String substringDate = defWorksheetD.getGroupDate().substring(defWorksheetD.getGroupDate().length() - 2);
				System.out.println(substringDate);
				months.add(substringDate);
				integers.add(defWorksheetD.getCountValue());
			});
			resultList.add(months);
			resultList.add(integers);
			resultMap.put("groupByMonthDayNum",resultList);
		}
		return JsonMapper.toJsonString(resultMap);
	}

Este combate real é inteiramente para uso, a cena não é muito boa

Na verdade, a solicitação assíncrona Ajax da recepção é paralela, adicionar este CompletableFuture assíncrono e paralelo nada mais é do que colocar várias solicitações Ajax juntas e, em seguida, retornar.

Devemos dizer onde estamos quase lá, o que economiza tempo de solicitação e resposta

Resumo dos métodos comumente usados ​​de CompletableFuture


//比较特殊,他入参就是返回值,也就是说他可以用来执行需要其他返回值的异步任务。
public static <U> CompletableFuture<U> completedFuture(U value)
 
//无返回值,使用默认线程池
public static CompletableFuture<Void> 	runAsync(Runnable runnable)
 
//无返回值,使用自定义线程池
public static CompletableFuture<Void> 	runAsync(Runnable runnable, Executor executor)
 
//有返回值,使用默认线程池
public static <U> CompletableFuture<U> 	supplyAsync(Supplier<U> supplier)
 
//有返回值,使用自定义线程池
public static <U> CompletableFuture<U> 	supplyAsync(Supplier<U> supplier, Executor executor)


//等待全部任务都结束,线程任务才全部结束
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs

//任何一个任务结束,线程任务全部结束
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) 


join()和get() 和getNow(T valueIfAbsent)  都是获取返回结果调用

1.join()方法抛出的是uncheck异常(即未经检查的异常),不会强制开发者抛出,
会将异常包装成CompletionException异常 /CancellationException异常,但是本质原因还是代码内存在的真正的异常,
2.get()方法抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch,
3.join() 和get()阻塞获取结果 都会一定拿到返回结果,除非异常
    
  getNow() 是非阻塞的,尝试获取结果,不一定能拿得到

Para outros métodos, consulte o  tutorial de entrada java8 CompletableFuture para explicar todos os métodos em detalhes com exemplos.  

resumo

Na verdade, existem muitos métodos CompletableFuture, como runAsync (), que é semelhante a supplyAsync (), mas não há valor de retorno; além de thenApply (), você pode adicionar uma função de retorno de chamada, há thenApply (); há também injeção runAfterBoth (), runAfterEither (), eles são bem conhecidos. Existem muitos mais, você pode clicar no código-fonte de CompletableFuture para ver mais de perto. Por meio de CompletableFuture, podemos sentir o poder do Java 8 ainda mais por meio de CompletableFuture. O poderoso conceito de fluxo, parametrização de comportamento, paralelismo eficiente, etc. não apenas torna o Java mais fácil de escrever, mas também enriquece continuamente todo o ecossistema Java. Java tem melhorado, por isso não foi eliminado com o tempo. Nós, Javaer, também podemos continuar nossa carreira. Graças a Java, podemos progredir juntos.

Acho que você gosta

Origin blog.csdn.net/zhaofuqiangmycomm/article/details/113320633
Recomendado
Clasificación