Java多线程的使用

166 阅读10分钟

多线程的使用

方法一:直接使用Thread

public static void main(String[] args) {
    Thread t1 = new Thread("t1") {
        public void run() {
            System.out.println("hello");
        }
    };
    t1.start();
}

方法二:使用 Runnable 配合 Thread

好处:对比方法一只要写一个任务可以被多个线程重用

public static void main(String[] args) {
    Runnable task2 = new Runnable() {
        @Override
        public void run() {
            System.out.println("hello");
        }
    };

    Thread t2 = new Thread(task2, "t2");
    t2.start();
}

方法三:FutureTask 配合 Thread

好处:对比方法二能获得返回值,也会抛出异常

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<Integer> task3 = new FutureTask<>(() -> {
        Thread.sleep(5000);
        System.out.println("t3 执行完");
        return 1;
    });

    new Thread(task3, "t3").start();

    Thread.sleep(2000);
    System.out.println("main 执行完");

    // 主现场阻塞等待同步的线程
    Integer integer = task3.get();
    System.out.println("结果是" + integer);
}

方法四:FutureTask配合线程池ThreadPool使用

这里用单线程的线程池来演示,换其它非单线程的线程池也一样的操作 好处:比方法三更能控制创建线程的数量,CPU的性能是有限的过多的创建线程并非一定性能提升

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

    ExecutorService ThreadPool = Executors.newSingleThreadExecutor();

    FutureTask<String> futureTask = new FutureTask<String>(() -> {
        try {
            TimeUnit.MICROSECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "task over";
    });

    ThreadPool.submit(futureTask);
}

方法五:CompletableFuture

Future的缺陷是,API太少,无非就是get()阻塞等待结果,或者isDone()轮询判断是否执行完成来减少阻塞。 如果想要做一些比如多个异步线程之间的关系,哪个线程前置,哪个线程需要等待前一个线程结果。就显得非常麻烦了。 其实如果对于线程的几种状态比较了解也能写出,无非就是改变线程状态控制线程之间的相互配合嘛。

但是写起来就比较麻烦且容易写错,那么你就会想能不能把常用的线程间配合的功能封装起来写成工具类。 不用了!有句话:在Java领域你永远都不会是第一个碰到这个问题的人!

已经有人做了且完善封装成标准工具了。

public static void main(String[] args) throws Exception {
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
        // 1 执行任务
        System.out.println(Thread.currentThread().getName() + "\t" + "-----come in");
        int result = ThreadLocalRandom.current().nextInt(10);
        //暂停几秒钟线程
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }).whenComplete((v, e) -> {
        // 2 执行完成后则
        if (e == null) {
            System.out.println("-----result: " + v);
        }
    }).exceptionally(e -> {
        // 3 若抛出异常则
        System.out.println("-----exception: " + e.getCause() + "\t" + e.getMessage());
        return -44;
    });

    // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

runAsync 无 返回值

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)  

supplyAsync 有 返回值

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor)

CompletableFuture常用方法 来到这里可能对比Future也看不出非常明显的使用优势,那是因为不知道它的一些常用方法

thenApply 与 thenCompose

thenApply 将上一阶段完成的结果作为当前阶段的入参,有返回值 thenCompose 用于组合多个CompletableFuture,将前一个任务的返回结果作为下一个任务的参数,它们之间存在着业务逻辑上的先后顺序。

thenApply()适用于对CompletableFuture调用的结果进行一些转换。

public static void main(String[] args) {
    CompletableFuture<String> completableFuture = CompletableFuture
            .supplyAsync(() -> "Hello")
            .thenApply(string -> string + " world");
    try {
        System.out.println(completableFuture.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

thenCompose()更适用于多个CompletableFuture调用的连接 意思是:对比前一个方法把返回的用CompletableFuture包装的解开包装继续串行异步

public static void main(String[] args) {
    CompletableFuture<String> completableFuture = CompletableFuture
            .supplyAsync(() -> "Hello")
            .thenCompose(string -> CompletableFuture.supplyAsync(() -> string + " world"));
    try {
        System.out.println(completableFuture.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

thenAccept 

消费前一阶段的结果,无返回值 (通常的终端操作)

CompletableFuture<Void> future = CompletableFuture
    .supplyAsync(() -> "Hello")
    .thenAccept((s) -> System.out.println(s + " world"));

thenRun 

当上一阶段完成后,执行本阶段的任务,无入参无反参 (通常的终端操作)

CompletableFuture<Void> future = CompletableFuture
    .supplyAsync(() -> "Hello")
    .thenRun(() -> System.out.println("do other things. 比如异步打印日志或发送消息"));

thenCombine 

合并另外一个任务,两个任务都完成后,执行BiFunction,入参为两个任务结果,返回新结果

CompletableFuture<List<DealGroupDTO>> getDeal(List<Integer> poiIds){
  return CompletableFuture.supplyAsync(() ->  poiService.queryPoiIds(poiIds));
}
//thenCompose
CompletableFuture<List<DealGroupDTO>> resultFuture = poiFuture.thenCompose(poiIds -> getDeal(poiIds));
resultFuture.get();

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
  .thenCombine(CompletableFuture.supplyAsync(() -> "world"), (s1, s2) -> s1 + s2);
//future.get()

exceptionally 

方法可以处理异步任务的异常,在出现异常时,给异步任务链一个从错误中恢复的机会

例子一:

Integer age = -1; 
CompletableFuture<Void> maturityFuture = CompletableFuture
    .supplyAsync(() -> { 
        if(age < 0) { 
            throw new IllegalArgumentException("Age can not be negative"); 
        } 
        if(age > 18) { 
            return "Adult"; 
        } else { 
            return "Child"; 
        } 
    }).exceptionally(ex -> { 
        System.out.println("Oops! We have an exception - " + ex.getMessage()); 
        return "Unknown!"; 
    }).thenAccept(s -> System.out.print(s));

例子二

public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                    System.out.println("1. 获取用户ID");
                    if (Math.random() > 0.8) throw new RuntimeException("ID服务宕机");
                    return "user123";
                }).thenCompose(userId -> CompletableFuture.supplyAsync(() -> {
                    System.out.println("2. 查询邮箱(基于 " + userId + ")");
                    if (Math.random() > 0.8) throw new RuntimeException("邮箱服务超时");
                    return userId + "@example.com";
                })).thenApply(email -> {
                    System.out.println("3. 格式化邮件内容");
                    if (Math.random() > 0.8) throw new RuntimeException("格式化失败");
                    return "欢迎, " + email;
                })
                // 👇 放在最后:捕获前面任意一步的异常
                .exceptionally(ex -> {
                    System.err.println("❌ 整个流程失败: " + ex.getMessage());
                    return "【系统繁忙,请稍后再试】";
                    // 兜底返回值
                });
        System.out.println("最终结果: " + future.join());
    }

handler

方法也可以处理异常,并且无论是否发生异常它都会被调用

Integer age = -1;

CompletableFuture<String> maturityFuture = CompletableFuture.supplyAsync(() -> {
    if(age < 0) {
        throw new IllegalArgumentException("Age can not be negative");
    }
    if(age > 18) {
        return "Adult";
    } else {
        return "Child";
    }
}).handle((res, ex) -> {
    if(ex != null) {
        System.out.println("Oops! We have an exception - " + ex.getMessage());
        return "Unknown!";
    }
    return res;
});

complete

是手动将一个 CompletableFuture 设置为“已完成”状态,并指定其结果值。

场景 1:手动控制异步任务的完成(外部触发)

你创建了一个 CompletableFuture,但不通过 supplyAsync 执行逻辑,而是等待某个事件(如回调、消息、定时器)来“手动完成”它。

    public static void main(String[] args) {
        CompletableFuture<String> future = new CompletableFuture<>();

        // 模拟:在另一个线程中,稍后完成这个 future
        new Thread(() -> {
            try {
                Thread.sleep(2000);
                boolean success = future.complete("来自外部的结果");
                System.out.println("完成成功?" + success); // true
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 主线程等待结果
        System.out.println("等待结果...");
        String result = future.join(); // 阻塞直到 complete 被调用
        System.out.println("结果: " + result);
    }

等待结果... 完成成功?true 结果: 来自外部的结果

场景 2 超时兜底 / 熔断

结合 orTimeout 或 anyOf,可以用 complete 实现超时返回默认值:

    public static void main(String[] args) {
        CompletableFuture<String> primary = CompletableFuture.supplyAsync(() -> {
            try { Thread.sleep(3000); } catch (Exception e) {}
            return "主服务结果";
        });

        CompletableFuture<String> fallback = new CompletableFuture<>();

        // 启动一个“超时守护线程”
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000); // 1秒超时
                fallback.complete("【超时,返回默认值】");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 谁先完成就用谁
        CompletableFuture<String> result = CompletableFuture.anyOf(primary, fallback)
                .thenApply(o -> (String) o);

        System.out.println(result.join()); // 很可能输出默认值
    }

completeExceptionally

如果任务没有完成,就抛出给定异常 (上面是标记'已完成'状态,这里是标记'异常'状态)

    public static void main(String[] args) {
        legacyApi.doSomething(new Callback() {
            public void onSuccess(Data d) {
                future.complete(d);
            }
            public void onError(Exception e) {
                future.completeExceptionally(e); // 传递异常
            }
        });
    }

anyOf

只要有任意一个 CompletableFuture 结束,就可以做接下来的事情

例子 : 3 个 future 中,future1 睡眠时间最短,会最先执行完成, anyOfFuture.get()获取的也就是 future1 的内容。future2、future3 的 返回结果被丢弃了

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

    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "future1 执行结果";
    });

    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "future2 执行结果";
    });


    CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "future3 执行结果";
    });

    CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(future1, future2, future3);
    System.out.println(anyOfFuture.get());
}

allOf

等待所有的 CompletableFuture 结束

例子 : 并行地下载 100 个网页。待下载完成之后,统计在 100 个网页中,含有某个单词的网页个数。

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class AllOfExample {
    public static void main(String[] args) {
        List<String> urls = Arrays.asList(
            "https://example.com",
            "https://httpbin.org/delay/1",
            "https://jsonplaceholder.typicode.com/posts/1"
        );

        // 1. 为每个 URL 启动一个异步下载任务
        List<CompletableFuture<String>> futures = urls.stream()
            .map(AllOfExample::downloadPage)
            .collect(Collectors.toList());

        // 2. 创建一个 future,它在 所有 下载任务都完成后才完成
        CompletableFuture<Void> allDone = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[0])
        );

        // 3. 等待所有任务完成
        allDone.join(); // 阻塞直到全部完成(或任意一个失败)

        // 4. 收集所有结果(注意:如果某个任务失败,join() 会抛异常)
        List<String> results = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());

        System.out.println("共下载 " + results.size() + " 个页面");
        results.forEach(content -> 
            System.out.println("- 内容长度: " + content.length())
        );
    }

    // 模拟异步下载(实际项目中可能是 HTTP 请求)
    private static CompletableFuture<String> downloadPage(String url) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("开始下载: " + url + " (线程: " + Thread.currentThread().getName() + ")");
            try {
                // 模拟网络延迟
                Thread.sleep(500 + (long)(Math.random() * 1000));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            return "Mock content from " + url; // 模拟返回内容
        });
    }
}

异步编排例子

1.三个线程异步顺序执行

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

public class OrderedExecutionWithOrchestration {

    public static void main(String[] args) throws Exception {
        // 创建一个固定大小的线程池(至少3个线程,体现“多线程”)
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 定义三个任务
        CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
            System.out.println("线程1开始执行 - " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("线程1执行完毕 - " + Thread.currentThread().getName());
        }, executor);

        // task2 在 task1 完成后执行
        CompletableFuture<Void> task2 = task1.thenRunAsync(() -> {
            System.out.println("线程2开始执行 - " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("线程2执行完毕 - " + Thread.currentThread().getName());
        }, executor);

        // task3 在 task2 完成后执行
        CompletableFuture<Void> task3 = task2.thenRunAsync(() -> {
            System.out.println("线程3开始执行 - " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("线程3执行完毕 - " + Thread.currentThread().getName());
        }, executor);

        // 等待整个编排链完成
        task3.join();

        executor.shutdown();
    }
}

2.线程1和2并发异步执行,线程3等待1和2完成再异步执行

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

public class ParallelThenJoinExecution {

    public static void main(String[] args) throws Exception {
        // 创建线程池(至少3个线程,确保1、2、3可由不同线程执行)
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 任务1:并发执行
        CompletableFuture<Void> task1 = CompletableFuture.runAsync(() -> {
            System.out.println("线程1开始执行 - " + Thread.currentThread().getName());
            try {
                Thread.sleep(2000); // 模拟耗时操作
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("线程1执行完毕 - " + Thread.currentThread().getName());
        }, executor);

        // 任务2:并发执行
        CompletableFuture<Void> task2 = CompletableFuture.runAsync(() -> {
            System.out.println("线程2开始执行 - " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000); // 耗时比任务1短
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("线程2执行完毕 - " + Thread.currentThread().getName());
        }, executor);

        // 任务3:等任务1和任务2都完成后再执行
        CompletableFuture<Void> task3 = CompletableFuture.allOf(task1, task2)
            .thenRunAsync(() -> {
                System.out.println("线程3开始执行 - " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("线程3执行完毕 - " + Thread.currentThread().getName());
            }, executor);

        // 等待整个流程结束
        task3.join();

        executor.shutdown();
    }
}

方法七:结合Spring的@Async

下面以一个简单接口为例子

注意:调用异步方法类上需要配置上注解@EnableAsync

不怎么推荐把@Async写到接口上的写法,最好把业务Service和异步Service分开

1 配置线程池

// 线程池配置类
@Slf4j
@Configuration
public class BootThreadpoolConfig {
 
    // 配置核心线程数
    private int corePoolSize = 5;
    // 配置最大线程数
    private int maxPoolSize = 20;
    // 配置任务队列的长度
    private int queueCapacity = 500;
    // 配置任务的空闲时间
    private int aliveSeconds = 600;
    // 配置线程前缀
    private String namePrefix = "localThreadPool";
    
    // 自定义线程池, 起个好记的名
    @Bean(name = "localBootAsyncExecutor")
    public Executor asyncServiceExecutor() {
        log.info("初始化 springboot 线程池");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        //配置线程的空闲时间
        executor.setKeepAliveSeconds(aliveSeconds);
 
        // RejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        log.info("springboot 线程池初始化完毕");
        return executor;
    }
 
 
}

2 接口 interface

package com.example.service;
 
import org.springframework.scheduling.annotation.Async;
 
 
public interface TestService {
    @Async("localBootAsyncExecutor")
    void test();
}

3 接口实现 implement

package com.example.service.impl;
 
import com.example.service.TestService;
import org.springframework.stereotype.Service;
 
@Service
public class TestServiceImpl implements TestService {
    @Override
    public void test() {
        System.out.println("ThreadName:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("测试Spring 异步调用!");
    }
}

4 controller

@RestController
@RequestMapping(value = "/test")
@EnableAsync
public class TestContoller {
 
    @Autowired
    private TestService testService;
 
    @GetMapping(value = "/testAsync")
    public void print() {
        System.out.println("ThreadName:" + Thread.currentThread().getName());
        System.out.println("当前线程开始执行测试函数......");
        testService.test();
        for (int i = 1; i <= 100; i++) {
            System.out.print(i + " ");
            if (i % 10 == 0) {
                System.out.println();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("当前线程测试函数执行完毕......");
    }
}

多线程事务问题。

用多线程会破坏事务的隔离性,速度和安全是鱼和熊掌的关系。

就算用了类似两段提交的原理实现了多线程的事务rollback,但是最后也会引出类似无法确保所有线程都一定提交的问题(这是两段提交2PC的缺陷,而分布式事务的最推荐的是不适用分布式事务)。

具体可以参考下面的链接中的多线程事务.

参考:

www.cnblogs.com/codechange/…

www.liaoxuefeng.com/wiki/125259…

cloud.tencent.com/developer/a…

blog.csdn.net/zsx_xiaoxin…

blog.csdn.net/lihaiyong92…

要我说,多线程事务它必须就是个伪命题! www.cnblogs.com/thisiswhy/p…