Android笔记:正确使用线程池及注意的地方

举报
程思扬 发表于 2022/01/14 00:28:12 2022/01/14
【摘要】 从业以来其实我一直对线程池接触的比较少,了解的比较潜,最近也看了看相关的知识,今天呢总结一下线程池的知识。 线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的...

从业以来其实我一直对线程池接触的比较少,了解的比较潜,最近也看了看相关的知识,今天呢总结一下线程池的知识。
线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的降低频繁创建和销毁线程对性能所带来的开销。

一、使用线程池有三个好处:

1、降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。

3、提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。

二、线程池的实现原理

当一个新任务提交到线程池时,简单来说线程池的处理流程如下:

#1、判断核心线程池里的线程是否都在执行任务,如果不是则创建一个新的工作线程处理任务,否则进入下个流程

#2、判断工作队列是否已满,如果未满则将新提交的任务存储在该工作队列,否则进入下个流程

#3、判断线程池里的线程是否都处于工作状态,如果不是则创建一个新的工作线程来执行任务,否则交由饱和策略处理

真正线程池的实现是通过ThreadPoolExecutor,ThreadPoolExecutor通过配置不同的参数配置来创建线程池。下面简单的介绍一下各个线程池的区别和用处。

三、线程池需要注意的问题

一般情况下会使用Executors创建线程池,目前不推荐,线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor方式,

这样的处理方式可以更加明确线程池的运行规则,规避资源耗尽的风险。

1、newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM

2、newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚

至OOM

ThreadPoolExecutor执行execute方法分以下4种情况:

#1、如果当前运行的线程少于corePoolSize,则创建新的核心线程来执行任务,当前步骤需获取全局锁

#2、如果运行的线程>=corePoolSize,则将任务加入阻塞队列

#3、如果队列已满,则创建新的非核心线程来处理任务,该步骤也需要获取全局锁

#4、如果创建新线程使当前运行的线程数>maxinumPoolSize,则任务将被拒绝并执行拒绝策略

四、常见的四种线程池

(1)fixThreadPool 正规线程

      我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。

  
 
  • 1
   public static ExecutorService newFixedThreadPool(int threads)
    {
    return newFixedThreadPool(threads,threads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }

  
 
  • 1
  • 2
  • 3
  • 4

核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。

(2)caCheThreadPool 缓存线程池

     只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

  
 
  • 1
   public static ExecutorService newCachedThreadPool(int threads)
    {
    return newFixedThreadPool(threads,Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }

  
 
  • 1
  • 2
  • 3
  • 4

(3)singleThreadPoll 单线程线程池

看这个名字就知道这个家伙是只有一个核心线程,就是一个孤家寡人,通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。确定就是一个人干活效率慢。

(4)ScheduledThreadPoll

     这个线程池就厉害了,是唯一一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。

  
 
  • 1

五、推荐的三种线程池创建方式

推荐方式1(使用了com.google.guava包)

    	ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                .setNameFormat("demo-pool-%d").build();
            ExecutorService executorService = new ThreadPoolExecutor(5, 10,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingDeque<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

推荐方式2(使用了commons-lang3包):

    	ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
            	new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

  
 
  • 1
  • 2

推荐方式3(spring配置线程池方式):自定义线程工厂bean需要实现ThreadFactory,可参考该接口的其它默认实现类,使用方式直接注入bean

调用execute(Runnable task)方法即可

        <!-- 添加默认实现 -->
        <bean id="threadFactory" class="java.util.concurrent.Executors$DefaultThreadFactory"/>
        <!-- 添加自定义实现 -->
        <bean id="threadFactoryNew" class="com.fc.provider.ThreadFactoryConsumer"/>
        <!-- 创建线程池 -->
        <bean id="userThreadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
            <property name="corePoolSize" value="5" />
            <property name="maxPoolSize" value="50" />
            <property name="queueCapacity" value="1000" />
            <property name="keepAliveSeconds" value="3000"/>
            <property name="threadFactory" ref="threadFactoryNew"/>
            <property name="rejectedExecutionHandler">
                <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
            </property>
        </bean>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

下面是完整示例:

public class ThreadPoolDemo {
 
    /**
     * 定义静态内部线程类
     */
    public static class MyTask implements Runnable {
 
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ":Thread name:"
                + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
    /**
     * @param args
     */
    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build();
        ExecutorService executorService = new ThreadPoolExecutor(5, 10,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingDeque<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 10; i++) {
            executorService.execute(myTask);
        }
        executorService.shutdown();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

线程池的分析和参考参考:http://ifeve.com/java-threadpool/

线程池和线程相关的详细介绍参考:http://www.cnblogs.com/dolphin0520/p/3932921.html
大家可根据自己的需求进行修改,同时文中有什么问题或建议可以指出,

文章来源: chengsy.blog.csdn.net,作者:程思扬,版权归原作者所有,如需转载,请联系作者。

原文链接:chengsy.blog.csdn.net/article/details/83150099

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。