JUC学习之同步队列和线程池

举报
多米诺的古牌 发表于 2021/09/26 16:11:30 2021/09/26
【摘要】 1.同步队列synchronousQueue和其他队列区别在于其不存储元素,put一个元素,必须take取出上一个才能再put进去,synchronousQueue只能有一个元素;2.线程池2.1 池化技术准备一些资源,有人使用就从这里拿取,等到用完了后,再换回来。2.2 优点2.2.1 降低资源的消耗因为创建和销毁非常消耗资源。2.2.2 提高响应速度不用一直新建销毁,节省了步骤。2.2....

1.同步队列

synchronousQueue和其他队列区别在于其不存储元素,put一个元素,必须take取出上一个才能再put进去,synchronousQueue只能有一个元素;

2.线程池

2.1 池化技术

准备一些资源,有人使用就从这里拿取,等到用完了后,再换回来。

2.2 优点

2.2.1 降低资源的消耗

因为创建和销毁非常消耗资源。

2.2.2 提高响应速度

不用一直新建销毁,节省了步骤。

2.2.3 方便管理

线程可以复用,可以控制最大并发数,可以管理线程。

3.线程池的具体了解

3.1 线程池的三大方法

3.1.1 创建单个线程的线程池

通过Executors工具类调用newSingleThreadExecutor()方法,创建只有一个固定的线程的线程池,下图为执行代码及结果:

public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName()+" ok");
            });
        }
 }

3.1.2 创建固定个数的线程池

通过Executors工具类调用newFixedThreadPool(创建的固定线程个数)方法,创建一个固定个数的线程的线程池,下图为执行创建有5个线程的线程池的代码及结果:

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

3.1.3 创建根据性能可伸缩个数的线程池

通过Executors工具类调用newCachedThreadPool()方法,创建一个个数根据硬件性能可伸缩线程个数的线程池,下图为执行创建可伸缩个数的线程池的代码及结果:

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

3.2 线程池的七大参数

3.2.1三大线程池创建方法源码解析

3.2.1.1 单个线程线程池

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

3.2.1.2 固定个数线程线程池

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

3.2.1.3 根据性能可伸缩线程个数线程池

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

3.2.1.4 ThreadPoolExecutor创建线程池

通过上面三大方法源码我们可以知道,三大方法的本质是通过ThreadPoolExecutor创建的线程池,只是传递的参数不同,下面我们详解下具体的参数:

3.2.1.4.1 int corePoolSize是核心线程的个数,只要创建线程池核心线程就会一直存在;
3.2.1.4.2 int maximumPoolSize是最大线程的个数,创建线程池后最大可以有多少线程存在;
3.2.1.4.3 long keepAliveTime是超时等待的时间(等候区闲置线程的存货时间),当队列(等候区)中闲置的时间,超过这个时间就会关闭除了核心线程之外的闲置线程;
3.2.1.4.4 TimeUnit unit是超时等待时间的单位;
3.2.1.4.5 BlockingQueue<Runnable> workQueue)是设置等候区的阻塞队列(即最大线程数-核心线程数,可以不指定);

3.2.1.4.6 ThreadFactory threadFactory是创建线程的线程工厂;
3.2.1.4.7 RejectedExecutionHandler handler是拒绝策略,即当设置的最大线程数时所采取的策略;

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {

 注1: 推荐使用ThreadPoolExecutor创建线程池,因为newFixedThreadPool和newCachedThreadPool方法最大线程是是允许达到Integer.MAX_VALUE(21亿)的,可能会堆积大量的请求或者创建大量的线程,会导致OOM。

注2:线程池创建结束后应手动关闭,一般放在finally之中。

threadPool.shutdown();

3.3 四种拒绝策略

3.3.1 AbortPolicy()在超过最大线程数的时候,不在处理新的线程,会抛异常java.util.concurrent.RejectedExecutionException

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
        3,
        3,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(3),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());
try {
    for (int i = 1; i <= 10; i++) {
        threadPool.execute(()->{
            System.out.println(Thread.currentThread().getName()+" ok");
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    threadPool.shutdown();
}

3.3.2 CallerRunsPolicy()策略是当线程达到最大的是时候,会将新增的线程扔给创建他的地方处理(下图为main方法中创建),不会抛出异常;

3.3.3 CallerRunsPolicy()策略,在达到最大线程数后,会丢弃新创建的线程,不抛异常;

3.3.4 DiscardOldestPolicy()策略,是在达到最大线程数后,尝试与最早的线程竞争(备胎的既视感。。。),如果竞争失败就被丢弃,不抛出异常。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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