多线程应用

why:利用多线程技术可以使系统同时运行多个程序块,缩短出程序响应的时间,提高计算机资源的利用率,达到多任务处理的目的

how:

1、继承Thread类或者实现Runnable接口

如果想有返回值:实现Callable接口,可以获取Future对象

2、线程池(推荐使用)

好处:省去创建和销毁时间,节约服务器资源,提升执行效率。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题

阿里公约推荐使用  ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端如下:

1)FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

示例:

@Configuration
public class ThreadPool {
    private static volatile ThreadPoolExecutor threadExecutorPool;
    @Bean(name = "threadExecutorPool")
    public ThreadPoolExecutor getImportCadExecutorPool() {
        if (threadExecutorPool == null) {
            synchronized (ThreadPool.class) {
                if (threadExecutorPool == null) {
                    threadExecutorPool = new ThreadPoolExecutor(
                        10, //核心线程数
                        10, //最大线程数
                        60, //空闲线程存活时间
                        TimeUnit.SECONDS, //时间单位
                        new LinkedBlockingQueue<Runnable>(1024), //线程池所使用的缓冲队列
                        new NamedThreadFactory("pool_name", false), //线程池创建线程使用的工厂,此处是线程池名称
                        new ThreadPoolExecutor.CallerRunsPolicy()); //线程池对拒绝任务的处理策略
                    }
                }
            }
            return threadExecutorPool;
      }
}

执行逻辑说明:

  • 判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务

  • 若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中

  • 若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务

  • 若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关

线程池拒绝策略:

      1、AbortPolicy

          直接丢弃任务,抛出RejectedExecutionException异常,是默认策略

      2、CallerRunsPolicy

          只用调用者所在的线程处理任务

      3、DiscardOldestPolicy

          丢弃等待队列中最旧的任务,并执行当前任务

      4、DiscardPolicy

          直接丢弃任务,但不抛出异常

3、java8新特性 parallelStream
   parallelStream的底层是基于ForkJoinPool线程池

   Fork/Join框架主要采用分而治之的理念来处理问题,对于一个比较大的任务,首先将它拆分(fork)为两个小任务task1与task2,最后将这两个任务结果合并
   ForkJoinPool线程池中是异步执行,且队列长度为AXIMUM_QUEUE_CAPACITY = 1 << 26  //67 108 864位,其更适合CPU密集型的任务使用。IO型任务容易OOM
   使用:
     推荐通过此方式获取一个公用线程池线程池
     ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
     可使用  System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","20");设置并行数
     也可以直接new ForkJoinPool();

关于线程安全:

1)常量始终是线程安全的,因为只存在读操作。

2)每次调用方法前都新建一个实例是线程安全的,因为不会访问共享的资源。

3)局部变量是线程安全的。因为每执行一个方法,都会在独立的空间创建局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。

  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LoadingCache是Guava库中的一个缓存工具类,用于高效地缓存数据。在多线程应用中,LoadingCache可以提供良好的并发支持。 首先,LoadingCache的内部实现是线程安全的。它使用了并发集合ConcurrentMap作为缓存的存储结构,并使用了同步机制来保证多线程并发的正确性。因此,在多线程环境下,不需要额外的加锁操作,不需要开发人员手动处理并发访问问题,而是由LoadingCache自身来处理。 其次,LoadingCache提供了get和getAll方法来获取缓存中的数据。在多线程应用中,当多个线程同时调用get方法来获取缓存中的数据时,LoadingCache可以保证只有一个线程去加载数据,其他线程会等待加载完成后直接从缓存中获取数据,避免了重复的数据加载操作。 另外,LoadingCache还提供了refresh方法,用于定时刷新缓存中的数据。在多线程应用中,可以通过设置定时任务或者使用定时调度框架,定期调用refresh方法来刷新缓存中的数据。这样可以保证在多线程并发操作下,缓存的数据始终是最新的。 需要注意的是,在多线程应用中,如果缓存的数据更新频繁,而且有多个线程在同时操作缓存,可能会导致缓存的命中率下降。这时可以考虑使用更加高级的缓存方案,如分布式缓存,来进一步提升缓存的并发性能和命中率。 综上所述,LoadingCache在多线程应用中可以提供良好的并发支持。它的线程安全的内部实现、合理的并发控制机制,以及定时刷新的功能,使得在多线程并发访问下,能够高效地缓存和获取数据,并保证数据的一致性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值