问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

案例分析:并行计算让代码“飞”起来

创作时间:
作者:
@小白创作中心

案例分析:并行计算让代码“飞”起来

引用
CSDN
1.
https://blog.csdn.net/2401_86608186/article/details/142071677

多线程编程是Java开发中必备的技能,尤其是在面试和实际工作中。本文将通过一个并行获取数据的案例,深入讲解多线程在业务场景中的应用,以及在开发过程中需要注意的关键事项。

并行获取数据

考虑以下场景:有一个用户数据接口,要求在50ms内返回数据。该接口需要从20多个其他接口汇总数据,而每个接口的最小耗时为20ms。如果串行处理,总耗时将远超50ms。因此,唯一可行的解决方案是采用并行处理,通过多线程同时获取计算结果,最后进行结果拼接。

幸运的是,Java提供了丰富的并发工具类,可以简化这类场景的开发。其中,CountDownLatch是一个非常适合此类场景的工具。它本质上是一个计数器,可以初始化为与执行任务相同的数量。当一个任务执行完成时,计数器的值减1,直到计数器值达到0时,表示完成了所有任务,在await上等待的线程就可以继续执行。

下面是一个专门为这个场景封装的工具类:

public class ParallelFetcher { 
    final long timeout; 
    final CountDownLatch latch; 
    final ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 1, 
            TimeUnit.HOURS, new ArrayBlockingQueue<>(100)); 
    public ParallelFetcher(int jobSize, long timeoutMill) { 
        latch = new CountDownLatch(jobSize); 
        timeout = timeoutMill; 
    } 
    public void submitJob(Runnable runnable) { 
        executor.execute(() -> { 
            runnable.run(); 
            latch.countDown(); 
        }); 
    } 
    public void await() { 
        try { 
            this.latch.await(timeout, TimeUnit.MILLISECONDS); 
        } catch (InterruptedException e) { 
            throw new IllegalStateException(); 
        } 
    } 
    public void dispose() { 
        this.executor.shutdown(); 
    } 
}

使用这个工具类的示例如下:

public static void main(String[] args) { 
    final String userid = "123"; 
    final SlowInterfaceMock mock = new SlowInterfaceMock(); 
    ParallelFetcher fetcher = new ParallelFetcher(20, 50); 
    final Map<String, String> result = new HashMap<>();  
    fetcher.submitJob(() -> result.put("method0", mock.method0(userid))); 
    fetcher.submitJob(() -> result.put("method1", mock.method1(userid))); 
    fetcher.submitJob(() -> result.put("method2", mock.method2(userid))); 
    fetcher.submitJob(() -> result.put("method3", mock.method3(userid))); 
    fetcher.submitJob(() -> result.put("method4", mock.method4(userid))); 
    fetcher.submitJob(() -> result.put("method5", mock.method5(userid))); 
    fetcher.submitJob(() -> result.put("method6", mock.method6(userid))); 
    fetcher.submitJob(() -> result.put("method7", mock.method7(userid))); 
    fetcher.submitJob(() -> result.put("method8", mock.method8(userid))); 
    fetcher.submitJob(() -> result.put("method9", mock.method9(userid))); 
    fetcher.submitJob(() -> result.put("method10", mock.method10(userid))); 
    fetcher.submitJob(() -> result.put("method11", mock.method11(userid))); 
    fetcher.submitJob(() -> result.put("method12", mock.method12(userid))); 
    fetcher.submitJob(() -> result.put("method13", mock.method13(userid))); 
    fetcher.submitJob(() -> result.put("method14", mock.method14(userid))); 
    fetcher.submitJob(() -> result.put("method15", mock.method15(userid))); 
    fetcher.submitJob(() -> result.put("method16", mock.method16(userid))); 
    fetcher.submitJob(() -> result.put("method17", mock.method17(userid))); 
    fetcher.submitJob(() -> result.put("method18", mock.method18(userid))); 
    fetcher.submitJob(() -> result.put("method19", mock.method19(userid)));  
    fetcher.await();  
    System.out.println(fetcher.latch); 
    System.out.println(result.size()); 
    System.out.println(result);  
    fetcher.dispose(); 
}

线程池的设置与优化

在上述代码中,线程池的设置是一个关键点。线程池的参数设置需要根据具体场景进行调整。例如,对于I/O密集型任务,线程数应该等于I/O任务的数量;而对于计算密集型任务,线程数应该等于CPU的数量。

从池化对象原理看线程池

线程池的构造方法包含多个参数,其中workQueuehandler是两个重要的参数。workQueue用于存储等待执行的任务,而handler则定义了当线程池饱和时的拒绝策略。

在SpringBoot中使用异步

在SpringBoot中,可以通过@EnableAsync@Async注解来实现异步任务。同时,建议自定义线程池以避免资源使用不可控的问题。

多线程资源盘点

线程安全的类

在多线程环境中,使用线程安全的类是非常重要的。例如,ConcurrentHashMap相对于HashMap具有更好的线程安全性。此外,ThreadLocal可以用于解决线程安全问题,例如在处理日期格式化时。

线程的同步方式

Java提供了多种线程同步方式,包括synchronized关键字、ReentrantLockvolatile关键字等。每种方式都有其适用场景和优缺点。

FastThreadLocal

Netty为了优化性能,创建了一个名为FastThreadLocal的结构。它通过改进底层数据结构来提高效率,并对缓存行进行了优化。

多线程使用中常见的问题

在多线程开发中,常见的问题包括线程池配置不当、异常处理不完善等。面试中,面试官经常会询问开发者在多线程使用中遇到的问题,以此来评估其实际应用能力。

异步的本质

异步编程通过将耗时操作转移到后台线程运行,从而减少对主业务的阻塞。它主要优化的是那些阻塞性的等待,能够起到转移冲突、优化请求响应的作用。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号