这个笔试题还是比较能检查出面试者的java的基础,如果有一定的java基础且有过一定的研究,这道题还是比较简单,但是,如果对多线程理解不深,则面试结果就不会理想。
对于这个具体实现,显然是有2种,一种是常规的for循环,另一种则是多线程。
第一种是保底的实现,就是for循环。
这个应该是大家直观可以想到的。
public class SumOfNumbers {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("Sum from 1 to 100 is: " + sum);
}
}
这个实现就不需要过多解释了,入门级实现。
第二种,使用线程池方式实现累加
线程池实现直接将难度提高至90分了。
目前线程池有好多种,比如:
-
newFixedThreadPool(int n): 创建一个固定大小的线程池。
-
newCachedThreadPool(): 创建一个可缓存的线程池。
-
newSingleThreadExecutor(): 创建一个单线程的线程池。
-
newScheduledThreadPool(int n): 创建一个支持定时和周期性任务的线程池。
当前的需求固定就是数量100,所以,我们可以确定了,就用固定大小的线程池就可以。
线程池的种类选择好了,然后就需要确定使用哪种具体API,因为我们是需要聚合计算,这一点很重要。另外,多线程环境下需要注意线程安全的问题。
直接看实现:
public class Demo1 {
// 使用ReentrantLock来保护对共享资源的访问
private static final Lock lock = new ReentrantLock();
private static int totalSum = 0;
public static void main(String[] args) throws InterruptedException, ExecutionException {
/**
* 创建一个固定大小的线程池 [100个总数】 所以线程池就是平分来算
*/
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 创建任务列表
List<Callable<Integer>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int start = i * 10 + 1;
int end = (i + 1) * 10;
tasks.add(new SumTask(start, end));
}
// 提交所有任务并获取结果
List<Future<Integer>> results = executorService.invokeAll(tasks);
// 汇总结果
for (Future<Integer> result : results) {
lock.lock();
try {
totalSum += result.get();
} finally {
lock.unlock();
}
}
// 关闭线程池
executorService.shutdown();
System.out.println("累加到100的结果: " + totalSum);
}
// 任务类,实现Callable接口
static class SumTask implements Callable<Integer> {
private final int start;
private final int end;
SumTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Integer call() {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
}
return sum;
}
}
}
这个就是多线程实现版本,代码可以直接跑。
这里面有几个关键点:
使用10个线程处理,每个线程分表处理10个数的和。然后再统计对10个结果的和进行累加。
因为需要返回线程结果,所以,使用Future。
比如
1到10,由线程1处理。
11到20,由线程2处理
以此类推
最后对10个结果进行聚合累加计算。
这里使用了显示锁。
如果在面试现场,没有机试的情况下,也可以写伪代码也行。
因为使用了线程池,面试中后续引出的问题就很多,
我们这里就说下Future。
Future接口,表示一个可能还没有完成异步任务的结果,它提供了检查任务是否已完成、以及等待任务完成并获取结果等方法。
当需要获取异步线程的执行结果返回值时,通常需要搭配使用Future和Callable接口来实现,大体可以用如下步骤来概括:
1.首先提交一个实现Callable接口的任务到线程池中
2.然后获取一个Future类型的对象
3.最后在主线程中调用Future对象的get()方法,如果异步任务执行完成,就可以直接获得结果;如果异步任务执行没有完成,get()方法会阻塞,直到任务执行完成后才能获取结果
我们重点来看下Future接口方法!
1、Future 接口方法
2、Future 接口实现类
Future本质其实是一个接口,并不是具体的实现类,真正负责工作的还是它的实现类来完成。
FutureTask类是一个实现了Future接口所有功能的具体类,可直接使用它来实现获取异步任务执行的结果值。
FutureTask的工作原理其实也并不复杂,它接受一个Callable或者Runnable对象作为参数,然后在线程池执行器中执行该任务,最后通过get()方法可以同步等待获取任务的执行结果。
真正起到关键作用的是,在FutureTask内部,封装了一个状态变量,用于记录任务的状态(等待、运行、完成、取消等),以及任务执行结果或异常信息,通过该状态变量,我们可以判断任务是否已完成、以及获取任务的执行结果等信息。
因为FutureTask也实现了Runnable接口,因此我们也可以将FutureTask作为任务,提交给线程池执行器。
同样,上面的多线程实现完成可以调整为使用FutureTask来实现。
如果感兴趣可以自己进行改造,我这里就不进行演示了。
了解技术点的底层逻辑,做到技术融汇贯通,那就像如鱼得水。
以上为全部内容。
更多技术内容,欢迎扫码关注10W+技术社区。