import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
// 定义函数式接口
@FunctionalInterface
interface Task {
String execute(int param);
}
public class MultiThreadTaskExecutor {
public static List<String> executeTasks(List<Integer> params) {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List<Future<String>> futures = new ArrayList<>();
// 提交任务到线程池
for (int param : params) {
Task task = (p) -> {
// 模拟任务执行
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Result for param " + p;
};
futures.add(executorService.submit(() -> task.execute(param)));
}
// 关闭线程池
executorService.shutdown();
// 收集结果
List<String> results = new ArrayList<>();
for (Future<String> future : futures) {
try {
results.add(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
return results;
}
public static void main(String[] args) {
List<Integer> params = List.of(1, 2, 3, 4, 5);
List<String> results = executeTasks(params);
System.out.println(results);
}
}
线程安全问题处理
- 线程池:使用
ExecutorService
创建线程池来管理线程,避免了线程的频繁创建和销毁。线程池本身是线程安全的,它会负责调度任务到不同的线程执行。
- 不可变数据:在任务执行过程中,尽量使用不可变数据。例如,任务接受的整数参数是不可变的,这样避免了多个线程对同一可变数据的竞争。
- 避免共享可变状态:在上述代码中,每个任务都是独立的,没有共享可变状态,从而减少了线程安全问题。如果确实需要共享状态,可以使用
java.util.concurrent.atomic
包中的原子类,或者使用ConcurrentHashMap
等线程安全的集合类。
函数式接口在此场景中的优势
- 简洁性:函数式接口允许使用Lambda表达式来创建任务实例,代码更加简洁。例如,
Task task = (p) -> {... }
,相比于传统的匿名内部类实现方式更加紧凑。
- 行为参数化:可以将不同的任务行为作为参数传递给方法。如果有不同类型的任务,只需要实现
Task
接口的execute
方法,就可以方便地在同一线程池框架下执行。
- 更好的可读性:Lambda表达式能够清晰地表达任务的逻辑,使代码更易于理解和维护。特别是在处理简单任务时,Lambda表达式的语义非常直观。