学习系列之并发编程(二)

线程池

池化技术

程序运行会占用系统资源,资源的不合理利用,会造成资源浪费,程序故障以及一系列内存问题,池化技术能够减少资源对象的创建次数,提高程序的性能,特别是在高并发下这种提高更加明显。比较常见的几种池化技术有:
线程池,连接池,内存池,常量池…
使用池化技术缓存的资源对象有如下共同特点:
1,对象创建时间长;2,对象创建需要大量资源;3,对象创建后可被重复使用。
通俗的讲,池化技术可以理解为:事先准备好一些资源,当有使用者需要使用的时候,可以直接拿去使用,但是在使用后要进行归还。

线程池

线程池的创建:
1.使用Executors创建

public class ThreadPoolTest {
    public static void main(String[] args) {

        //创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
       //创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
      //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 
       ExecutorService threadPool = Executors.newCachedThreadPool();
      //创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。 
      ExecutorService threadPool = Executors.newScheduledThreadPool(2);
        try {
            for (int i = 0; i < 10; i++) {

                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" run ..");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }

    }
}

注:阿里巴巴开发规范中,不允许使用Executors来创建线程池,而是推荐使用ThreadPoolExecutor去创建,查阅资料后,发现Executors各个方法有如下弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

2.使用 ThreadPoolExecutor去创建
查看ThreadPoolExecutor源码,发现有如下构造方法
在这里插入图片描述
以最多参数为例,

public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
                              int maximumPoolSize,//最大核心线程池大小
                              long keepAliveTime,//超时没人调用就释放
                              TimeUnit unit,//超时单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,一般不变
                              RejectedExecutionHandler handler//拒绝策略)

参数说明:
corePoolSize:线程池的核心线程数;
maximumPoolSize:线程池的最大线程数;
keepAliveTime:线程池空闲时线程的存活时长;
unit:线程存活时长大单位,结合上个参数使用;
workQueue:存放任务的队列,使用的是阻塞队列;
threadFactory:线程池创建线程的工厂;
handler:在队列(workQueue)和线程池达到最大线程数。

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {


        ExecutorService threadPool = new ThreadPoolExecutor(
                2,5,3,
                TimeUnit.SECONDS,new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try{
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" run ..");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

扩展:
1.拒绝策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
2.任务缓存队列
即workQueue,它用来存放等待执行的任务。
workQueue的类型为BlockingQueue,通常可以取下面三种类型:
1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

在这里插入图片描述
图片引用:狂神说java
总结:
其实这两种方式在本质上是一种方式,通过查看Executors源码发现其最终都是通过ThreadPoolExecutor类的方式。所以ThreaPoolExecutor是重点。

参考资料:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值