[Java 并发基础]多线程编程

文章参考:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html
https://juejin.cn/post/6970558076642394142

文章目录

  • 线程的创建方式
    • 继承 `Thread`
    • 实现 `Runnable` 接口
    • 实现 `Callable` 接口
    • 使用 `Lambda`
    • 使用线程池
  • 线程创建相关的 `jdk`源码
    • `Thread`类
    • `Runnable`函数接口
    • `Callable<V>`函数接口
    • `executors`
  • 线程扩展知识
    • `future`
      • _Method Summary_
      • 简单示例代码
    • `CompletableFuture`
      • `All MethodsStatic MethodsInstance MethodsConcrete Methods`
      • 简单示例代码
    • CompletableFuture使用场景
      • 创建异步任务
        • supplyAsync方法
        • runAsync方法
      • 任务异步回调
        • 1. thenRun/thenRunAsync
        • 2.thenAccept/thenAcceptAsync
        • 3. thenApply/thenApplyAsync
        • 4. exceptionally
        • 5. whenComplete方法
        • 6. handle方法
      • 多个任务组合处理
        • AND组合关系
        • OR 组合的关系
      • AllOf
      • AnyOf
      • thenCompose
    • CompletableFuture使用有哪些注意点
      • 1. Future需要获取返回值,才能获取异常信息
      • 2. CompletableFuture的get()方法是阻塞的。
      • 3. 默认线程池的注意点
      • 4. 自定义线程池时,注意饱和策略

线程的创建方式

继承 Thread

  1. 创建一个继承 Thread 类的子类。
  2. 重写 Thread 类的 run() 方法。
  3. 在 run() 方法中编写线程要执行的任务。
  4. 创建 Thread 子类的对象。
  5. 调用 Thread 子类对象的 start() 方法来启动线程。
public class Demo {static class MyThread extends Thread {@Overridepublic void run() {System.out.println(">>>> run ");}}public static void main(String[] args) {new MyThread().start();}
}

实现 Runnable 接口

  1. 创建一个实现 Runnable 接口的类。
  2. 在 Runnable 接口的 run() 方法中编写线程要执行的任务。
  3. 创建 Runnable 接口的实现类的对象。
  4. 将 Runnable 接口的实现类的对象传递给 Thread 类的构造方法来创建 Thread 对象。
  5. 调用 Thread 对象的 start() 方法来启动线程。
package org.example.create;public class Demo1 {static class MyThread1 implements Runnable {@Overridepublic void run() {System.out.println(">>>>> 2. 实现Runnable接口");}}public static void main(String[] args) throws Exception {new MyThread1().run();System.out.println("end");}
}

实现 Callable 接口

package org.example.create;import java.util.concurrent.Callable;public class Demo2 {static class MyThread implements Callable<Void> {@Overridepublic Void call() throws Exception {System.out.println("3. 实现 Callable ");return null;}}public static void main(String[] args) throws Exception {new MyThread().call();System.out.println("end");}
}

使用 Lambda

package org.example.create;public class Demo3 {public static void main(String[] args) throws Exception {new Thread(()->{System.out.println("4. 使用 lambda 表达式");}).run();System.out.println("end");}
}

使用线程池

package org.example.create;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo4 {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(5);// 创建Runnable对象Runnable runnable = () -> {// 线程的执行逻辑System.out.println(Thread.currentThread().getId() + "线程执行逻辑");};// 提交任务给线程池for (int i = 0; i < 50; i++) {executor.submit(runnable);}// 关闭线程池executor.shutdown();}}

线程创建相关的 jdk源码

Thread

package java.lang;public class Thread implements Runnable {/* Make sure registerNatives is the first thing <clinit> does. */private static native void registerNatives();static {registerNatives();}
}

Runnable函数接口

package java.lang;
/**
The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.
This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.
In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.
Since:
1.0
See Also:
Thread, java.util.concurrent.Callable**/
@FunctionalInterface
public interface Runnable {/*** When an object implementing interface {@code Runnable} is used* to create a thread, starting the thread causes the object's* {@code run} method to be called in that separately executing* thread.* <p>* The general contract of the method {@code run} is that it may* take any action whatsoever.** @see     java.lang.Thread#run()*/public abstract void run();
}

Callable<V>函数接口

package java.util.concurrent;
/**
A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.
The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.
The Executors class contains utility methods to convert from other common forms to Callable classes.
Since:
1.5
See Also:
Executor
Author:
Doug Lea
Type parameters:
<V> – the result type of method call
**/
@FunctionalInterface
public interface Callable<V> {/*** Computes a result, or throws an exception if unable to do so.** @return computed result* @throws Exception if unable to compute a result*/V call() throws Exception;
}

executors

package java.util.concurrent;
/**
Factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes defined in this package. This class supports the following kinds of methods:
Methods that create and return an ExecutorService set up with commonly useful configuration settings.
Methods that create and return a ScheduledExecutorService set up with commonly useful configuration settings.
Methods that create and return a "wrapped" ExecutorService, that disables reconfiguration by making implementation-specific methods inaccessible.
Methods that create and return a ThreadFactory that sets newly created threads to a known state.
Methods that create and return a Callable out of other closure-like forms, so they can be used in execution methods requiring Callable.
Since:
1.5
Author:
Doug Lea
**/
public class Executors {}

线程扩展知识

future

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html

java.util.concurrent
Interface Future<V>Type Parameters:V - The result type returned by this Future's get methodAll Known Subinterfaces:Response<T>,RunnableFuture<V>,RunnableScheduledFuture<V>,ScheduledFuture<V>All Known Implementing Classes:
CompletableFuture, CountedCompleter, ForkJoinTask, FutureTask, RecursiveAction, RecursiveTask, SwingWorker

public interface Future<V>

A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future<?> and return null as a result of the underlying task.
Sample Usage (Note that the following classes are all made-up.)

interface ArchiveSearcher { String search(String target); }
class App {ExecutorService executor = ...ArchiveSearcher searcher = ...void showSearch(final String target)throws InterruptedException {Future<String> future= executor.submit(new Callable<String>() {public String call() {return searcher.search(target);}});displayOtherThings(); // do other things while searchingtry {displayText(future.get()); // use future} catch (ExecutionException ex) { cleanup(); return; }}
}

The FutureTask class is an implementation of Future that implements Runnable, and so may be executed by an Executor. For example, the above construction with submit could be replaced by:

FutureTask<String> future =
new FutureTask<String>(new Callable<String>() {public String call() {return searcher.search(target);}});
executor.execute(future);

Memory consistency effects: Actions taken by the asynchronous computation happen-before actions following the corresponding Future.get() in another thread.

内存一致性影响:异步计算所采取的操作发生在另一个线程中对应的Future.get()之后的操作之前。

Method Summary

Modifier and TypeMethod and Description
booleancancel(boolean mayInterruptIfRunning)
Attempts to cancel execution of this task.
Vget()
Waits if necessary for the computation to complete, and then retrieves its result.
Vget(long timeout, TimeUnit unit)
Waits if necessary for at most the given time for the computation to complete, and then retrieves its result, if available.
booleanisCancelled()
Returns true if this task was cancelled before it completed normally.
booleanisDone()
Returns true if this task completed.

简单示例代码

package org.example.future;import org.example.service.MedalService;
import org.example.service.UserInfoService;import java.util.concurrent.*;public class FutureTest {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(10);UserInfoService userInfoService = new UserInfoService();MedalService medalService = new MedalService();long userId = 666L;long startTime = System.currentTimeMillis();//调用用户服务获取用户基本信息FutureTask<UserInfoService.UserInfo> userInfoFutureTask = new FutureTask<>(new Callable<UserInfoService.UserInfo>() {@Overridepublic UserInfoService.UserInfo call() throws Exception {return userInfoService.getUserInfo(userId);}});executorService.submit(userInfoFutureTask);Thread.sleep(300); //模拟主线程其它操作耗时FutureTask<MedalService.MedalInfo> medalInfoFutureTask = new FutureTask<>(new Callable<MedalService.MedalInfo>() {@Overridepublic MedalService.MedalInfo call() throws Exception {return medalService.getMedalInfo(userId);}});executorService.submit(medalInfoFutureTask);UserInfoService.UserInfo userInfo = userInfoFutureTask.get();//获取个人信息结果MedalService.MedalInfo medalInfo = medalInfoFutureTask.get();//获取勋章信息结果System.out.println(userInfo);System.out.println(medalInfo);System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");executorService.shutdown();}
}

CompletableFuture

异步编程利器:CompletableFuture详解 |Java 开发实战 - 掘金
image.png

java.lang.Object
java.util.concurrent.CompletableFuture<T>
All Implemented Interfaces:
CompletionStage<T>, Future<T>public class CompletableFuture<T>
extends Object
implements Future<T>, CompletionStage<T>

A Future that may be explicitly completed (setting its value and status), and may be used as a CompletionStage, supporting dependent functions and actions that trigger upon its completion.
When two or more threads attempt to complete, completeExceptionally, or cancel a CompletableFuture, only one of them succeeds.

可以显式完成的Future(设置其值和状态),可以用作CompletionStage,支持在其完成时触发的依赖函数和操作。
当两个或多个线程尝试完成、completeException或取消一个CompletableFuture时,只有一个线程成功。

In addition to these and related methods for directly manipulating status and results, CompletableFuture implements interface CompletionStage with the following policies:

  • Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.

为非异步方法的依赖完成提供的操作可以由完成当前CompletableFuture的线程执行,也可以由完成方法的任何其他调用者执行。

  • All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interface CompletableFuture.AsynchronousCompletionTask.

所有没有显式Executor参数的异步方法都使用 ForkJoinPool.commonPool() 执行(除非它不支持至少两个并行级别,在这种情况下,创建一个新线程来运行每个任务)。为了简化监视、调试和跟踪,所有生成的异步任务都是标记接口 CompletableFuture.AsynchronousCompletionTask 的实例。

  • All CompletionStage methods are implemented independently of other public methods, so the behavior of one method is not impacted by overrides of others in subclasses.

所有CompletionStage方法的实现都独立于其他公共方法,因此一个方法的行为不受子类中其他方法的覆盖的影响。

CompletableFuture also implements Future with the following policies:

  • Since (unlike FutureTask) this class has no direct control over the computation that causes it to be completed, cancellation is treated as just another form of exceptional completion. Method cancel has the same effect as completeExceptionally(new CancellationException()). Method isCompletedExceptionally() can be used to determine if a CompletableFuture completed in any exceptional fashion.

由于(与FutureTask不同)该类对导致其完成的计算没有直接控制,因此取消被视为另一种形式的异常完成。方法 cancelcompleteException (new CancellationException()) 具有相同的效果。方法 isCompletedException() 可用于确定 CompletableFuture 是否以任何异常方式完成。

  • In case of exceptional completion with a CompletionException, methods get() and get(long, TimeUnit) throw an ExecutionException with the same cause as held in the corresponding CompletionException. To simplify usage in most contexts, this class also defines methods join() and getNow(T) that instead throw the CompletionException directly in these cases.

在使用 CompletionException 异常完成的情况下,方法 get()get(long, TimeUnit) 抛出一个 ExecutionException,其原因与对应的 CompletionException 中持有的原因相同。
为了简化大多数上下文中的使用,这个类还定义了 join()getNow(T) 方法,在这些情况下直接抛出 CompletionException

All MethodsStatic MethodsInstance MethodsConcrete Methods

Modifier and Type	Method and DescriptionCompletableFuture<Void>	acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed with the corresponding result as argument to the supplied action.CompletableFuture<Void>	acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed using this stage's default asynchronous execution facility, with the corresponding result as argument to the supplied action.CompletableFuture<Void>	acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed using the supplied executor, with the corresponding result as argument to the supplied function.static CompletableFuture<Void>	allOf(CompletableFuture<?>... cfs)
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete.static CompletableFuture<Object>	anyOf(CompletableFuture<?>... cfs)
Returns a new CompletableFuture that is completed when any of the given CompletableFutures complete, with the same result.<U> CompletableFuture<U>	applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed with the corresponding result as argument to the supplied function.<U> CompletableFuture<U>	applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed using this stage's default asynchronous execution facility, with the corresponding result as argument to the supplied function.<U> CompletableFuture<U>	applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T,U> fn, Executor executor)
Returns a new CompletionStage that, when either this or the other given stage complete normally, is executed using the supplied executor, with the corresponding result as argument to the supplied function.boolean	cancel(boolean mayInterruptIfRunning)
If not already completed, completes this CompletableFuture with a CancellationException.boolean	complete(T value)
If not already completed, sets the value returned by get() and related methods to the given value.static <U> CompletableFuture<U>	completedFuture(U value)
Returns a new CompletableFuture that is already completed with the given value.boolean	completeExceptionally(Throwable ex)
If not already completed, causes invocations of get() and related methods to throw the given exception.CompletableFuture<T>	exceptionally(Function<Throwable,? extends T> fn)
Returns a new CompletableFuture that is completed when this CompletableFuture completes, with the result of the given function of the exception triggering this CompletableFuture's completion when it completes exceptionally; otherwise, if this CompletableFuture completes normally, then the returned CompletableFuture also completes normally with the same value.T	get()
Waits if necessary for this future to complete, and then returns its result.T	get(long timeout, TimeUnit unit)
Waits if necessary for at most the given time for this future to complete, and then returns its result, if available.T	getNow(T valueIfAbsent)
Returns the result value (or throws any encountered exception) if completed, else returns the given valueIfAbsent.int	getNumberOfDependents()
Returns the estimated number of CompletableFutures whose completions are awaiting completion of this CompletableFuture.<U> CompletableFuture<U>	handle(BiFunction<? super T,Throwable,? extends U> fn)
Returns a new CompletionStage that, when this stage completes either normally or exceptionally, is executed with this stage's result and exception as arguments to the supplied function.<U> CompletableFuture<U>	handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
Returns a new CompletionStage that, when this stage completes either normally or exceptionally, is executed using this stage's default asynchronous execution facility, with this stage's result and exception as arguments to the supplied function.<U> CompletableFuture<U>	handleAsync(BiFunction<? super T,Throwable,? extends U> fn, Executor executor)
Returns a new CompletionStage that, when this stage completes either normally or exceptionally, is executed using the supplied executor, with this stage's result and exception as arguments to the supplied function.boolean	isCancelled()
Returns true if this CompletableFuture was cancelled before it completed normally.boolean	isCompletedExceptionally()
Returns true if this CompletableFuture completed exceptionally, in any way.boolean	isDone()
Returns true if completed in any fashion: normally, exceptionally, or via cancellation.T	join()
Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally.void	obtrudeException(Throwable ex)
Forcibly causes subsequent invocations of method get() and related methods to throw the given exception, whether or not already completed.void	obtrudeValue(T value)
Forcibly sets or resets the value subsequently returned by method get() and related methods, whether or not already completed.CompletableFuture<Void>	runAfterBoth(CompletionStage<?> other, Runnable action)
Returns a new CompletionStage that, when this and the other given stage both complete normally, executes the given action.CompletableFuture<Void>	runAfterBothAsync(CompletionStage<?> other, Runnable action)
Returns a new CompletionStage that, when this and the other given stage complete normally, executes the given action using this stage's default asynchronous execution facility.CompletableFuture<Void>	runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor)
Returns a new CompletionStage that, when this and the other given stage complete normally, executes the given action using the supplied executor.CompletableFuture<Void>	runAfterEither(CompletionStage<?> other, Runnable action)
Returns a new CompletionStage that, when either this or the other given stage complete normally, executes the given action.CompletableFuture<Void>	runAfterEitherAsync(CompletionStage<?> other, Runnable action)
Returns a new CompletionStage that, when either this or the other given stage complete normally, executes the given action using this stage's default asynchronous execution facility.CompletableFuture<Void>	runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor)
Returns a new CompletionStage that, when either this or the other given stage complete normally, executes the given action using the supplied executor.static CompletableFuture<Void>	runAsync(Runnable runnable)
Returns a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() after it runs the given action.static CompletableFuture<Void>	runAsync(Runnable runnable, Executor executor)
Returns a new CompletableFuture that is asynchronously completed by a task running in the given executor after it runs the given action.static <U> CompletableFuture<U>	supplyAsync(Supplier<U> supplier)
Returns a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() with the value obtained by calling the given Supplier.static <U> CompletableFuture<U>	supplyAsync(Supplier<U> supplier, Executor executor)
Returns a new CompletableFuture that is asynchronously completed by a task running in the given executor with the value obtained by calling the given Supplier.CompletableFuture<Void>	thenAccept(Consumer<? super T> action)
Returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied action.CompletableFuture<Void>	thenAcceptAsync(Consumer<? super T> action)
Returns a new CompletionStage that, when this stage completes normally, is executed using this stage's default asynchronous execution facility, with this stage's result as the argument to the supplied action.CompletableFuture<Void>	thenAcceptAsync(Consumer<? super T> action, Executor executor)
Returns a new CompletionStage that, when this stage completes normally, is executed using the supplied Executor, with this stage's result as the argument to the supplied action.<U> CompletableFuture<Void>	thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
Returns a new CompletionStage that, when this and the other given stage both complete normally, is executed with the two results as arguments to the supplied action.<U> CompletableFuture<Void>	thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action)
Returns a new CompletionStage that, when this and the other given stage complete normally, is executed using this stage's default asynchronous execution facility, with the two results as arguments to the supplied action.<U> CompletableFuture<Void>	thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action, Executor executor)
Returns a new CompletionStage that, when this and the other given stage complete normally, is executed using the supplied executor, with the two results as arguments to the supplied function.<U> CompletableFuture<U>	thenApply(Function<? super T,? extends U> fn)
Returns a new CompletionStage that, when this stage completes normally, is executed with this stage's result as the argument to the supplied function.<U> CompletableFuture<U>	thenApplyAsync(Function<? super T,? extends U> fn)
Returns a new CompletionStage that, when this stage completes normally, is executed using this stage's default asynchronous execution facility, with this stage's result as the argument to the supplied function.<U> CompletableFuture<U>	thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
Returns a new CompletionStage that, when this stage completes normally, is executed using the supplied Executor, with this stage's result as the argument to the supplied function.<U,V> CompletableFuture<V>	thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
Returns a new CompletionStage that, when this and the other given stage both complete normally, is executed with the two results as arguments to the supplied function.<U,V> CompletableFuture<V>	thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
Returns a new CompletionStage that, when this and the other given stage complete normally, is executed using this stage's default asynchronous execution facility, with the two results as arguments to the supplied function.<U,V> CompletableFuture<V>	thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)
Returns a new CompletionStage that, when this and the other given stage complete normally, is executed using the supplied executor, with the two results as arguments to the supplied function.<U> CompletableFuture<U>	thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
Returns a new CompletionStage that, when this stage completes normally, is executed with this stage as the argument to the supplied function.<U> CompletableFuture<U>	thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
Returns a new CompletionStage that, when this stage completes normally, is executed using this stage's default asynchronous execution facility, with this stage as the argument to the supplied function.<U> CompletableFuture<U>	thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn, Executor executor)
Returns a new CompletionStage that, when this stage completes normally, is executed using the supplied Executor, with this stage's result as the argument to the supplied function.CompletableFuture<Void>	thenRun(Runnable action)
Returns a new CompletionStage that, when this stage completes normally, executes the given action.CompletableFuture<Void>	thenRunAsync(Runnable action)
Returns a new CompletionStage that, when this stage completes normally, executes the given action using this stage's default asynchronous execution facility.CompletableFuture<Void>	thenRunAsync(Runnable action, Executor executor)
Returns a new CompletionStage that, when this stage completes normally, executes the given action using the supplied Executor.CompletableFuture<T>	toCompletableFuture()
Returns this CompletableFuture.String	toString()
Returns a string identifying this CompletableFuture, as well as its completion state.CompletableFuture<T>	whenComplete(BiConsumer<? super T,? super Throwable> action)
Returns a new CompletionStage with the same result or exception as this stage, that executes the given action when this stage completes.CompletableFuture<T>	whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
Returns a new CompletionStage with the same result or exception as this stage, that executes the given action using this stage's default asynchronous execution facility when this stage completes.CompletableFuture<T>	whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
Returns a new CompletionStage with the same result or exception as this stage, that executes the given action using the supplied Executor when this stage completes.

简单示例代码

package org.example.future;import org.example.service.MedalService;
import org.example.service.UserInfoService;import java.util.concurrent.*;import static org.example.service.MedalService.MedalInfo;
import static org.example.service.UserInfoService.UserInfo;public class CompletableFutureTest {public static void main(String[] args) throws ExecutionException, TimeoutException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(10);UserInfoService userInfoService = new UserInfoService();MedalService medalService = new MedalService();long userId = 666L;long startTime = System.currentTimeMillis();//调用用户服务获取用户基本信息CompletableFuture<UserInfo> completableUserInfoFuture = CompletableFuture.supplyAsync(() -> userInfoService.getUserInfo(userId), executorService);Thread.sleep(300); //模拟主线程其它操作耗时CompletableFuture<MedalInfo> completableMedalInfoFuture = CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(userId), executorService);UserInfo userInfo = completableUserInfoFuture.get(2, TimeUnit.SECONDS);//获取个人信息结果MedalInfo medalInfo = completableMedalInfoFuture.get();//获取勋章信息结果System.out.println(userInfo);System.out.println(medalInfo);System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");executorService.shutdown();}
}

CompletableFuture使用场景

创建异步任务

CompletableFuture创建异步任务,一般有supplyAsync和runAsync两个方法

  • supplyAsync执行CompletableFuture任务,支持返回值
  • runAsync执行CompletableFuture任务,没有返回值。
supplyAsync方法
//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
//自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync方法
//使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable) 
//自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)

实例代码如下:

public class FutureTest {public static void main(String[] args) {//可以自定义线程池ExecutorService executor = Executors.newCachedThreadPool();//runAsync的使用CompletableFuture<Void> runFuture = CompletableFuture.runAsync(() -> System.out.println("run,关注公众号:捡田螺的小男孩"), executor);//supplyAsync的使用CompletableFuture<String> supplyFuture = CompletableFuture.supplyAsync(() -> {System.out.print("supply,关注公众号:捡田螺的小男孩");return "捡田螺的小男孩"; }, executor);//runAsync的future没有返回值,输出nullSystem.out.println(runFuture.join());//supplyAsync的future,有返回值System.out.println(supplyFuture.join());executor.shutdown(); // 线程池需要关闭}
}
//输出
run,关注公众号:捡田螺的小男孩
null
supply,关注公众号:捡田螺的小男孩捡田螺的小男孩

任务异步回调

1. thenRun/thenRunAsync
public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);

CompletableFuture的thenRun方法,通俗点讲就是,做完第一个任务后,再做第二个任务。某个任务执行完成后,执行回调方法;但是前后两个任务没有参数传递,第二个任务也没有返回值

public class FutureThenRunTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("先执行第一个CompletableFuture方法任务");return "捡田螺的小男孩";});CompletableFuture thenRunFuture = orgFuture.thenRun(() -> {System.out.println("接着执行第二个任务");});System.out.println(thenRunFuture.get());}
}
//输出
先执行第一个CompletableFuture方法任务
接着执行第二个任务
null

thenRun 和thenRunAsync有什么区别呢?可以看下源码哈:

java
复制代码   private static final Executor asyncPool = useCommonPool ?
ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}public CompletableFuture<Void> thenRunAsync(Runnable action) {
return uniRunStage(asyncPool, action);
}

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

  • 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池
  • 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池

TIPS: 后面介绍的thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是这个哈。

2.thenAccept/thenAcceptAsync

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

csharp
复制代码public class FutureThenAcceptTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("原始CompletableFuture方法任务");return "捡田螺的小男孩";});CompletableFuture thenAcceptFuture = orgFuture.thenAccept((a) -> {if ("捡田螺的小男孩".equals(a)) {System.out.println("关注了");}System.out.println("先考虑考虑");});System.out.println(thenAcceptFuture.get());}
}
3. thenApply/thenApplyAsync

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

csharp
复制代码public class FutureThenApplyTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("原始CompletableFuture方法任务");return "捡田螺的小男孩";});CompletableFuture<String> thenApplyFuture = orgFuture.thenApply((a) -> {if ("捡田螺的小男孩".equals(a)) {return "关注了";}return "先考虑考虑";});System.out.println(thenApplyFuture.get());}
}
//输出
原始CompletableFuture方法任务
关注了
4. exceptionally

CompletableFuture的exceptionally方法表示,某个任务执行异常时,执行的回调方法;并且有抛出异常作为参数,传递到回调方法。

php
复制代码public class FutureExceptionTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("当前线程名称:" + Thread.currentThread().getName());throw new RuntimeException();});CompletableFuture<String> exceptionFuture = orgFuture.exceptionally((e) -> {e.printStackTrace();return "你的程序异常啦";});System.out.println(exceptionFuture.get());}
}
//输出
当前线程名称:ForkJoinPool.commonPool-worker-1
java.util.concurrent.CompletionException: java.lang.RuntimeExceptionat java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.lang.RuntimeExceptionat cn.eovie.future.FutureWhenTest.lambda$main$0(FutureWhenTest.java:13)at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)... 5 more
你的程序异常啦
5. whenComplete方法

CompletableFuture的whenComplete方法表示,某个任务执行完成后,执行的回调方法,无返回值;并且whenComplete方法返回的CompletableFuture的result是上个任务的结果

csharp
复制代码public class FutureWhenTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("当前线程名称:" + Thread.currentThread().getName());try {Thread.sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}return "捡田螺的小男孩";});CompletableFuture<String> rstFuture = orgFuture.whenComplete((a, throwable) -> {System.out.println("当前线程名称:" + Thread.currentThread().getName());System.out.println("上个任务执行完啦,还把" + a + "传过来");if ("捡田螺的小男孩".equals(a)) {System.out.println("666");}System.out.println("233333");});System.out.println(rstFuture.get());}
}
//输出
当前线程名称:ForkJoinPool.commonPool-worker-1
当前线程名称:ForkJoinPool.commonPool-worker-1
上个任务执行完啦,还把捡田螺的小男孩传过来
666
233333
捡田螺的小男孩
6. handle方法

CompletableFuture的handle方法表示,某个任务执行完成后,执行回调方法,并且是有返回值的;并且handle方法返回的CompletableFuture的result是回调方法执行的结果。

csharp
复制代码public class FutureHandlerTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> orgFuture = CompletableFuture.supplyAsync(()->{System.out.println("当前线程名称:" + Thread.currentThread().getName());try {Thread.sleep(2000L);} catch (InterruptedException e) {e.printStackTrace();}return "捡田螺的小男孩";});CompletableFuture<String> rstFuture = orgFuture.handle((a, throwable) -> {System.out.println("上个任务执行完啦,还把" + a + "传过来");if ("捡田螺的小男孩".equals(a)) {System.out.println("666");return "关注了";}System.out.println("233333");return null;});System.out.println(rstFuture.get());}
}
//输出
当前线程名称:ForkJoinPool.commonPool-worker-1
上个任务执行完啦,还把捡田螺的小男孩传过来
666
关注了

多个任务组合处理

AND组合关系


thenCombine / thenAcceptBoth / runAfterBoth都表示:将两个CompletableFuture组合起来,只有这两个都正常执行完了,才会执行某个任务
区别在于:

  • thenCombine:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
  • thenAcceptBoth: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • runAfterBoth 不会把执行结果当做方法入参,且没有返回值。
arduino
复制代码public class ThenCombineTest {public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {CompletableFuture<String> first = CompletableFuture.completedFuture("第一个异步任务");ExecutorService executor = Executors.newFixedThreadPool(10);CompletableFuture<String> future = CompletableFuture//第二个异步任务.supplyAsync(() -> "第二个异步任务", executor)// (w, s) -> System.out.println(s) 是第三个任务.thenCombineAsync(first, (s, w) -> {System.out.println(w);System.out.println(s);return "两个异步任务的组合";}, executor);System.out.println(future.join());executor.shutdown();}
}
//输出
第一个异步任务
第二个异步任务
两个异步任务的组合
OR 组合的关系


applyToEither / acceptEither / runAfterEither 都表示:将两个CompletableFuture组合起来,只要其中一个执行完了,就会执行某个任务。
区别在于:

  • applyToEither:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
  • acceptEither: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • runAfterEither: 不会把执行结果当做方法入参,且没有返回值。
kotlin
复制代码public class AcceptEitherTest {public static void main(String[] args) {//第一个异步任务,休眠2秒,保证它执行晚点CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{try{Thread.sleep(2000L);System.out.println("执行完第一个异步任务");}catch (Exception e){return "第一个任务异常";}return "第一个异步任务";});ExecutorService executor = Executors.newSingleThreadExecutor();CompletableFuture<Void> future = CompletableFuture//第二个异步任务.supplyAsync(() -> {System.out.println("执行完第二个任务");return "第二个任务";}, executor)//第三个任务.acceptEitherAsync(first, System.out::println, executor);executor.shutdown();}
}
//输出
执行完第二个任务
第二个任务

AllOf

所有任务都执行完成后,才执行 allOf返回的CompletableFuture。如果任意一个任务异常,allOf的CompletableFuture,执行get方法,会抛出异常

kotlin
复制代码public class allOfFutureTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> a = CompletableFuture.runAsync(()->{System.out.println("我执行完了");});CompletableFuture<Void> b = CompletableFuture.runAsync(() -> {System.out.println("我也执行完了");});CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(a, b).whenComplete((m,k)->{System.out.println("finish");});}
}
//输出
我执行完了
我也执行完了
finish

AnyOf

任意一个任务执行完,就执行anyOf返回的CompletableFuture。如果执行的任务异常,anyOf的CompletableFuture,执行get方法,会抛出异常

csharp
复制代码public class AnyOfFutureTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> a = CompletableFuture.runAsync(()->{try {Thread.sleep(3000L);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("我执行完了");});CompletableFuture<Void> b = CompletableFuture.runAsync(() -> {System.out.println("我也执行完了");});CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(a, b).whenComplete((m,k)->{System.out.println("finish");
//            return "捡田螺的小男孩";});anyOfFuture.join();}
}
//输出
我也执行完了
finish

thenCompose

thenCompose方法会在某个任务执行完成后,将该任务的执行结果,作为方法入参,去执行指定的方法。该方法会返回一个新的CompletableFuture实例

  • 如果该CompletableFuture实例的result不为null,则返回一个基于该result新的CompletableFuture实例;
  • 如果该CompletableFuture实例为null,然后就执行这个新任务
arduino
复制代码public class ThenComposeTest {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> f = CompletableFuture.completedFuture("第一个任务");//第二个异步任务ExecutorService executor = Executors.newSingleThreadExecutor();CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "第二个任务", executor).thenComposeAsync(data -> {System.out.println(data); return f; //使用第一个任务作为返回}, executor);System.out.println(future.join());executor.shutdown();}
}
//输出
第二个任务
第一个任务

CompletableFuture使用有哪些注意点

CompletableFuture 使我们的异步编程更加便利的、代码更加优雅的同时,我们也要关注下它,使用的一些注意点。

1. Future需要获取返回值,才能获取异常信息

ini
复制代码ExecutorService executorService = new ThreadPoolExecutor(5, 10, 5L,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {int a = 0;int b = 666;int c = b / a;return true;},executorService).thenAccept(System.out::println);//如果不加 get()方法这一行,看不到异常信息//future.get();

Future需要获取返回值,才能获取到异常信息。如果不加 get()/join()方法,看不到异常信息。小伙伴们使用的时候,注意一下哈,考虑是否加try…catch…或者使用exceptionally方法。

2. CompletableFuture的get()方法是阻塞的。

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

csharp
复制代码//反例CompletableFuture.get();
//正例
CompletableFuture.get(5, TimeUnit.SECONDS);

3. 默认线程池的注意点

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

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

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

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

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

相关文章

专业145+总分420+电子科技大学858信号与系统考研经验电子信息与通信

今年考研各门都相对发挥比较好&#xff0c;总分420&#xff0c;专业858信号与系统145&#xff0c;数学135顺利上岸电子科技大学&#xff0c;应群里很多学弟学妹要求&#xff0c;我总结一下自己的复习经验&#xff0c;希望可以在考研路上&#xff0c;助大家一臂之力。专业课&…

软考正式改为机考,对于考生有哪些影响?

1、报名费用可能会涨价 考虑到报名费用问题&#xff0c;软考初级考生可能会担心费用是否会涨价。需要指出的是&#xff0c;并非所有地区都会涨价&#xff0c;只有部分地区有可能涨价。 2、机考考试难度会降低吗&#xff1f; 如果实施机考&#xff0c;将是一次重大改革&#…

云计算概述(云计算类型、技术驱动力、关键技术、特征、特点、通用点、架构层次)(二)

云计算概述&#xff08;二&#xff09; &#xff08;云计算类型、技术驱动力、关键技术、特征、特点、通用点、架构层次&#xff09; 目录 零、00时光宝盒 一、云计算类型&#xff08;以服务的内容或形态来分) 二、云计算的12种技术驱动力 三、云计算的关键技术 四、云计…

Fiddler修改https请求与响应 bug修复变灰了选不了等 Fiddle对夜神模拟器抓包设置

不要修改别人的东西&#xff0c;不要修改别人的东西&#xff0c;不要修改别人的东西 只用于自己的网站&#xff0c;自己安全调试。 fiddler修改https请求 1、打到要改的请求 2、替换请求内容 3、开启捕获。操作产生请求。 4、fiddler里查看请求或响应数据 &#xff0c;确认成…

05 - 什么是路由协议

1 路由协议 路由协议&#xff08;英语&#xff1a;Routing protocol&#xff09;&#xff1a; 是一种指定数据包转送方式的网上协议。Internet网络的主要节点设备是路由器&#xff0c;路由器通过路由表来转发接收到的数据。 路由协议&#xff0c;根据转发策略进行分类&#xff…

探索智慧文旅:科技如何提升游客体验

随着科技的迅猛发展&#xff0c;智慧文旅已成为旅游业的重要发展方向。通过运用先进的信息技术&#xff0c;智慧文旅不仅改变了传统旅游业的运营模式&#xff0c;更在提升游客体验方面取得了显著成效。本文将深入探讨科技如何助力智慧文旅提升游客体验。 一、智慧文旅的兴起与…

Android开发学习-中级控件

Drawable Android把所有能够显示的图形都抽象为Drawable类(可绘制的)。 这里的图形不止是图片&#xff0c;还包括色块、画板、背景等。 包含图片在内的图形文件放在res目录的各个drawable目录下&#xff0c;其中drawable目录一般保存描述性的XML文件&#xff0c;而图片文件一…

MySQL原理(三)锁定机制(1)综述

一、介绍&#xff1a; 1、锁的本质 业务场景中存在共享资源&#xff0c;多个进程或线程需要竞争获取并处理共享资源&#xff0c;为了保证公平、可靠、结果正确等业务逻辑&#xff0c;要把并发执行的问题变为串行&#xff0c;串行时引入第三方锁当成谁有权限来操作共享资源的判…

idea/webstorm 创建Vue实例 Unresolved type Vue 处理方法

1.电脑本地安装node.js 官网下载 2. 其他: 未排除变量,前期试错(以下步骤配置了,但不确定对解决问题是否有帮助)

C# .Net Framework webapi 全局日志

1.创建一个类名字叫做CustomActionFilter.cs /// <summary>/// /// </summary>public class CustomActionFilter : System.Web.Http.Filters.ActionFilterAttribute{/// <summary>/// /// </summary>/// <param name"actionExecutedContext&q…

C++ 入门(三)— 函数

文章目录 函数简介函数返回值Void 函数&#xff08;非值返回函数&#xff09;函数参数和参数局部范围函数的声明和定义具有多个代码文件的程序 函数简介 C 程序的方式工作。当程序遇到函数调用时&#xff0c;它将在一个函数内按顺序执行语句。函数调用是告诉 CPU 中断当前函数…

AI的安全应答之道

作者&#xff1a;统信UOS技术团队 2023,随着各种大语言模型的爆发&#xff0c;整个AI生态正处于从决策式AI进化到生成式AI的进程中。各类AI模型和AI应用层出不穷&#xff0c;也随之带来了与AI相关的各类潜在风险。AI开发和使用过程中的风险防范和治理&#xff0c;成为了不可忽…

【20240131】USB相机(查看设备列表、打开设备)

USB相机采集 1、v4l2查看设备列表2、查看具体设备信息3、在桌面打开USB相机 1、v4l2查看设备列表 打开终端&#xff0c;输入&#xff1a;v4l2-ctl --list-devices usb设备在Webcam: Webcam栏&#xff0c;分别是video9和video10&#xff0c;下一步&#xff1a;确定哪一个是接入…

vue2将一个页面作为组件来展示

这里使用vue2进行的实现 首先先建一个页面 <template><div>我是标题块111</div></template><script> export default {name: "index" } </script><style scoped></style>建好了页面&#xff0c;接下来在需要将这个页…

华为mate60 pro与小米14 pro 的巅峰对决

今天我们换下思路&#xff0c;不讲技术了&#xff01;我们一起讲讲手机&#xff01;小编暂时充当一下业余的数码咖。 今天我们就讲讲华为mate60 pro和小米14pro 这两款手机。这两款手机都是近期新出的发行版本&#xff0c;热度那是一直未减啊。 华为mate60 Pro 我们先说说这个…

分布式搜索引擎_学习笔记_1

分布式搜索引擎01 – elasticsearch基础 0.学习目标 1.初识elasticsearch 1.1.了解ES 1.1.1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎&#xff0c;具备非常多强大功能&#xff0c;可以帮助我们从海量数据中快速找到需要的内容 例如&#xff1a; …

01、全文检索 ------ 反向索引库 与 Lucene 的介绍

目录 全文检索 ------ 反向索引库 与 LuceneSQL模糊查询的问题反向索引库反向索引库的查询 Lucene&#xff08;全文检索技术&#xff09;Lucene能做什么Lucene存在的问题Solr 和 Elasticsearch 与 Lucene 的关系 全文检索 ------ 反向索引库 与 Lucene MySQL一些索引词汇解释 …

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?

我记得前年&#xff08;2022&#xff09;面试的时候有被问到 ViewPager 和 ViewPager2 有什么区别&#xff1f;当时因为之前工作一直在开发售货机相关的项目&#xff0c;使用的技术要求并不高&#xff0c;所以一直没去了解过 ViewPager2~ 去年的时候正好有相关的功能需求&#…

09. BI - 数据可视化,如何进行基本图形绘制

本文为 「茶桁的 AI 秘籍 - BI 篇 第 09 篇」 文章目录 EDA 作用可视化视图Python 进行可视化subplot Hi&#xff0c;你好。我是茶桁。 今天想给大家讲的是关于数据的可视化。在工作中很多时候我们不光要计算结果&#xff0c;还要把结果呈现出来&#xff0c;最好是一种图形化的…

机器学习_无监督学习之聚类

文章目录 介绍机器学习下的分类K均值算法K值的选取:手肘法用聚类辅助理解营销数据贴近项目实战 介绍机器学习下的分类 以下介绍无监督学习之聚类 聚类是最常见的无监督学习算法。人有归纳和总结的能力&#xff0c;机器也有。聚类就是让机器把数据集中的样本按照特征的性质分组&…