我正在编写一个搜索函数,该函数正在执行多个 API 调用,我希望异步执行并收集它们的结果。我的所有线程及其可运行对象看起来都很相似,这让我们想知道我是否可以将这些线程封装到一个方法中,因为每个线程只更改 2 行。
看起来与此类似:
List<BookResponse> allMatches = Collections.synchronizedList(new ArrayList<>());
List<Thread> threads = new ArrayList<>();
Thread searchBookName = new Thread(() -> {
try {
String someParam = someMethod();
List<BookResponse> matches = fetchMethod(someParam);
synchronized (allMatches) {
allMatches.addAll(matches);
}
} catch (Exception e) {
throw new CustomException(e);
}
});
threads.add(searchBookName);
searchBookName.start();
Thread searchBookContent = new Thread(() -> {
try {
int intParam = anyMethod();
List<BookResponse> matches = differentFetchMethod(intParam);
synchronized (allMatches) {
allMatches.addAll(matches);
}
} catch (Exception e) {
throw new CustomException(e);
}
});
threads.add(searchBookContent);
searchBookContent.start();
/*
*
*
* More Threads...
*
*
*/
for (Thread search : searches) {
search.join();
}
return new ResponseEntity<List<BookResponse>>(allMatches, HttpStatus.OK);
这些线程块占用了代码中的大量空间并且非常重复,它们是按照这种模式制作的,只有 2 条注释行发生了变化:
Thread searchSomething = new Thread(() -> {
try {
//Always 1 method call for a param, params are different types
//Always 1 api call giving back results
synchronized (allMatches) {
allMatches.addAll(matches);
}
} catch (Exception e) {
throw new CustomException(e);
}
});
threads.add(searchSomething);
searchSomething.start();
我试图想出一个接口来解决这个问题,但我最终不得不以某种方式实现这两行代码,所以我并没有使代码变得更干净。
您没有创建或子类化应该创建 Runnable 的线程(另请参阅 Future、Callable、Executor 和线程池)。
abstract class AbstractBookSearcher<ParamType> implements Runnable {
private final ParamType searchParam;
AbstractBookSearcher(ParamType searchParam) {
this.searchParam = searchParam;
}
public void run() throws Exception {
try {
List<BookResponse> matches = search(searchParam);
synchronized (allMatches) {
allMatches.addAll(matches);
}
} catch (Exception e) {
throw new CustomException(e);
}
}
protected abstract List<BookResponse> search(ParamType searchParam);
}
然后子类化:
class BookNameSearcher extends AbstractBookSearcher<String> {
protected abstract List<BookResponse> search(String searchParam) {
return fetchMethod(searchParam);
}
}
或:
class BookContentSearcher extends AbstractBookSearcher<Integer> {
protected abstract List<BookResponse> search(Integer searchParam) {
return differentFetchMethod(searchParam);
}
}
然后您可以使用自定义和类型安全的可运行对象以及所需的搜索参数启动线程:
new Thread(new BookNameSearcher(someMethod()));
new Thread(new BookContentSearcher(anyMethod()));
可以使用 java 8 的 lambda 函数来避免参数数量及其类型的强绑定。
我们需要使用不同数量的不同类型的参数来制作
fetchMethod(String someParam)
。
fetchMethod(int someParam, String someParam2)
private void executeAllThreads() {
List<BookResponse> allMatches = Collections.synchronizedList(new ArrayList<>());
List<Thread> threads = new ArrayList<>();
Supplier<List<BookResponse>> searchBookNameSupplier = ()->fetchMethod(5);
Thread searchBookName = getThread(searchBookNameSupplier, allMatches);
threads.add(searchBookName);
searchBookName.start();
Supplier<List<BookResponse>> bookResponseSupplier = ()->differentFetchMethod("stringParam");
Thread searchBookContent = getThread(bookResponseSupplier,allMatches);
threads.add(searchBookContent);
searchBookContent.start();
/*
*
*
* More Threads...
*
*
*/
for (Thread search : searches) {
search.join();
}
}
private Thread getThread(Supplier<List<BookResponse>> bookResponseSupplier, List<BookResponse> allMatches) {
Thread thread = new Thread(() -> {
try {
List<BookResponse> matches = bookResponseSupplier.get();
synchronized (allMatches) {
allMatches.addAll(matches);
}
} catch (Exception e) {
throw new CustomException(e);
}
});
return thread;
}
重构时还有几点需要考虑。
Executors.newCacheThreadPool().submit(searchBookName)
join()ing
所有线程之后使用callable和addAll(),以避免使用synchronizedList是的,将相同线程的制作构建到生产方法中是一个好主意。它鼓励代码的可重用性、可读性和可靠性,从而产生更加模块化和高效的软件。Fm Whatsapp