大厂面试题:写一个1加到100求和的类,五分钟内完成。怎么才能写出即简单又高深的实现?

这个笔试题还是比较能检查出面试者的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+技术社区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值