一文看懂多线程(超详细+附代码)
Java 创建线程的 5种主要方式⚠️ 本质线程真正实体是Thread类业务逻辑放在run()start () 才是真正开启线程直接调用 run () 只是普通方法执行不会新建线程1. 继承 Thread 类类继承Thread重写run()new 子类对象调用start()class MyThread extends Thread{ Override public void run() { System.out.println(线程执行); } } //使用 MyThread t new MyThread(); t.start();缺点Java 单继承占用继承名额不灵活。传入接口重写run()把 Runnable 实例传入 Thread 构造器调用start()class MyRunnable implements Runnable{ Override public void run() { System.out.println(runnable线程); } } //使用 Thread t new Thread(new MyRunnable()); t.start();✅优点1. 避免单继承限制适合资源共享也可以用 lambda 简写new Thread(()- System.out.println(lambda线程)).start();2. 可以继承别的类3. 多个线程共享同一个任务⚠️注意区分线程Thread vs 任务RunnableRunnable执行什么Thread谁执行示例Runnable 多线程共享同一个任务关键点多个 Thread 对象传入同一个 Runnable 实例就实现多线程共享同一个任务对象。 注意任务里的成员变量会被多个线程同时修改会出现线程安全问题。public class ShareRunnableDemo { static class MyTask implements Runnable { // 共享变量多个线程同时操作要注意并发问题 private int count 10; Override public void run() { while(true){ synchronized (this){ if(count 0) break; System.out.println(Thread.currentThread().getName() 剩余 count); count--; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { // 【同一个任务对象】 MyTask task new MyTask(); // 多个线程共用这一个task Thread t1 new Thread(task, 线程A); Thread t2 new Thread(task, 线程B); Thread t3 new Thread(task, 线程C); t1.start(); t2.start(); t3.start(); } }输出特点不会出现重复数字不会出现负数数字一定从 10 → 1 依次全部输出一遍。但是顺序不固定哪个线程抢到锁哪个线程打印。 示例一种可能输出线程A 剩余10 线程B 剩余9 线程C 剩余8 线程A 剩余7 线程C 剩余6 线程B 剩余5 线程A 剩余4 线程C 剩余3 线程B 剩余2 线程A 剩余1也可能是 A/B/C 乱序穿插但是数字一定是 10,9,8…1每个数字出现恰好一次。3. 实现 Callable FutureTask可以获取返回值、抛异常Runnable 的 run 无返回值不能抛出受检异常Callable 可以拿到线程执行结果。CallableInteger callable ()-{ return 100; //返回结果 }; FutureTaskInteger task new FutureTask(callable); Thread t new Thread(task); t.start(); Integer res task.get(); //阻塞等待获取返回值CallableV定义任务返回值FutureTaskV任务 结果容器get()等待线程执行完成4. 线程池方式生产环境优先使用通过ExecutorService线程池创建线程不手动 new Thread复用线程ExecutorService pool Executors.newFixedThreadPool(2); pool.execute(new Runnable() { Override public void run() { System.out.println(线程池任务); } }); //带返回值提交 FutureInteger future pool.submit(()- 66);5. CompletableFuture异步线程返回结果Java8新增前面 CallableFuture 的痛点get()阻塞拿结果没有回调无法任务编排任务 A 做完再做 BA、B 都完成做 CA 或 B 任意一个完成就执行不能手动完成任务。CompletableFuture解决上面全部问题。1. 创建 CompletableFuture 的几种常用 API① runAsync无返回值// 默认使用ForkJoinPool.commonPool公共线程池 CompletableFutureVoid future CompletableFuture.runAsync(() - { System.out.println(异步执行无返回); }); // 指定自定义线程池生产环境强烈建议不要用公共池 CompletableFuture.runAsync(()-{ System.out.println(自定义线程池执行); }, executorService);② supplyAsync有返回值// 有返回结果 CompletableFutureString f CompletableFuture.supplyAsync(()-{ return 任务结果; });⚠️重点不传线程池时默认用ForkJoinPool.commonPool()IO 密集型任务不要用公共池公共池所有 CompletableFuture 共享IO 阻塞会占满公共池影响整个应用其他异步任务。2. 任务编排核心能力面试重点thenApply拿到上一步结果转换有返回CompletableFutureString f1 CompletableFuture.supplyAsync(()-hello) .thenApply(s - s world);thenAccept拿到结果消费无返回CompletableFuture.supplyAsync(()-ok) .thenAccept(s - System.out.println(s));thenRun上一步完成后执行拿不到上一步结果CompletableFuture.supplyAsync(()-ok) .thenRun(() - System.out.println(任务执行完毕));thenCompose任务嵌套把上一步结果返回新 CompletableFuture异步链式// 上一步返回CompletableFuture用于嵌套异步 CompletableFutureString res CompletableFuture.supplyAsync(()-100) .thenCompose(num - CompletableFuture.supplyAsync(()- num _str));thenCombine两个独立任务都完成合并两者结果CompletableFutureString f1 CompletableFuture.supplyAsync(()-A); CompletableFutureString f2 CompletableFuture.supplyAsync(()-B); CompletableFutureString combine f1.thenCombine(f2, (a,b)- a b);anyOf任意一个任务完成就继续CompletableFutureObject any CompletableFuture.anyOf(f1,f2);allOf所有任务全部完成才继续CompletableFutureVoid all CompletableFuture.allOf(f1,f2);3. 异常处理exceptionally捕获异常给兜底返回值CompletableFuture.supplyAsync(()-{ int i 1/0; return ok; }).exceptionally(ex - { ex.printStackTrace(); return 兜底异常返回; });whenComplete正常 / 异常都会进入回调可以拿到结果或异常不改变返回值future.whenComplete((res,ex)-{ if(ex ! null){ //处理异常 } });handle正常或者异常都可以并且可以重新返回结果future.handle((res,ex)-{ if(ex!null){ return 出错了; } return res; });4. 面试关键区分CompletableFuture 不是新的线程创建方式底层还是线程池ForkJoinPool 或者自定义 ExecutorthenXxx()和thenXxxAsync()区别thenApply使用上一步执行完成的线程执行回调thenApplyAsync交给线程池重新拿一个线程执行回调公共池坑IO 耗时任务一定要传入自定义线程池否则公共线程耗尽全应用 CompletableFuture 卡死allOf不返回各个任务结果需要自己遍历每个 future 拿结果anyOf返回 Object需要强转。和老版 Future 对比表格FutureCompletableFuture获取结果get () 阻塞阻塞 get / 非阻塞回调任务编排无链式、组合、并行、或 / 与等待异常处理只能 get 抛异常exceptionally / handle / whenComplete手动完成不支持complete () 手动设置结果面试口述一句话总结CompletableFuture 是 Future 的增强基于线程池实现异步任务编排支持回调、多任务组合、异常处理它本身不创建线程底层依赖线程池IO 场景不要直接用默认 ForkJoin 公共池。