一、创建线程

1.创建普通对象,只是在JVM的堆里分配一块内存而已

2.创建线程,需要调用操作系统内核的API,然后操作系统需要为线程分配一系列资源,成本很高

  • 线程是一个重量级对象,应该避免频繁创建和销毁,采用线程池方案

二、一般的池化资源

// 假设Java线程池采用一般意义上池化资源的设计方法
class ThreadPool {
    // 获取空闲线程
    Thread acquire() {
    }
    // 释放线程
    void release(Thread t) {
    }
}
// 期望的使用
ThreadPool pool;
Thread T1 = pool.acquire();
// 传入Runnable对象
T1.execute(() -> {
    // 具体业务逻辑
});

三、生产者-消费者模式

业界线程池的设计,普遍采用生产者-消费者模式,线程池的使用方是生产者,线程池本身是消费者

public class MyThreadPool {
    // 工作线程负责消费任务并执行任务
    class WorkerThread extends Thread {
        @Override
        public void run() {
            // 循环取任务并执行
            while (true) {
                Runnable task = null;
                try {
                    task = workQueue.take();
                } catch (InterruptedException e) {
                }
                task.run();
            }
        }
    }

    // 利用阻塞队列实现生产者-消费者模式
    private BlockingQueue<Runnable> workQueue;
    // 内部保存工作线程
    List<WorkerThread> threads = new ArrayList<>();

    public MyThreadPool(int poolSize, BlockingQueue<Runnable> workQueue) {
        this.workQueue = workQueue;
        for (int i = 0; i < poolSize; i++) {
            WorkerThread work = new WorkerThread();
            work.start();
            threads.add(work);
        }
    }

    // 提交任务
    public void execute(Runnable command) throws InterruptedException {
        workQueue.put(command);
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建有界阻塞队列
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
        // 创建线程池
        MyThreadPool pool = new MyThreadPool(10, workQueue);
        // 提交任务
        pool.execute(() -> {
            System.out.println("hello");
        });
    }
}

四、Java线程池

Ⅰ. ThreadPoolExecutor

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

// 让所有线程都支持超时,如果线程池很闲,那么将撤销所有线程
public void allowCoreThreadTimeOut(boolean value)

1.corePoolSize:线程池保有的最小线程数

2.maximumPoolSize:线程池创建的最大线程数

3.keepAliveTime & unit

  • 如果一个线程空闲了keepAliveTime & unit,并且线程池的线程数大于corePoolSize,那么这个空闲的线程就要被回收

4.workQueue:工作队列

5.threadFactory:自定义如何创建线程

6.handler

  • 线程池中的所有线程都很忙碌,并且工作队列也满了(工作队列是有界队列),此时提交任务,线程池会拒绝接收
  • CallerRunsPolicy:提交任务的线程自己去执行该任务
  • AbortPolicy:默认的拒绝策略,抛出RejectedExecutionException
  • DiscardPolicy:直接丢弃任务,不会抛出任何异常
  • DiscardOldestPolicy:丢弃最老的任务,然后把新任务加入到工作队列中

Ⅱ. Executors

1.不建议使用Executors,因为Executors提供的很多默认方法使用的是无界队列LinkedBlockingQueue

2.在高负载的情况下,无界队列容易导致OOM,而OOM会导致所有请求都无法处理

3.因此强烈建议使用有界队列

Ⅲ. 拒绝策略

1.使用有界队列,当任务过多时,线程池会触发拒绝策略

2.线程池默认的拒绝策略会抛出RejectedExecutionException,这是一个运行时异常,开发时很容易忽略

3.如果线程池处理的任务非常重要,可以自定义拒绝策略

Ⅳ. 异常处理

1.使用ThreadPoolExecutor.execute()方法提交任务时,如果任务在执行过程中出现运行时异常

  • 会导致执行任务的线程终止,并且无法获得任何通知

2.因此最稳妥的方法还是捕获所有异常并处理

try {
    // 业务逻辑
} catch (RuntimeException x) {
    // 按需处理
} catch (Throwable x) {
    // 按需处理
}

写在最后

Java并发——阿里架构师是如何巧用线程池的!的更多相关文章

  1. java并发编程(十七)Executor框架和线程池

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17465497   Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动 ...

  2. Java并发编程原理与实战三十七:线程池的原理与使用

    一.简介 线程池在我们的高并发环境下,实际应用是非常多的!!适用频率非常高! 有过使用过Executors框架的朋友,可能不太知道底层的实现,这里就是讲Executors是由ThreadPoolExe ...

  3. Java并发(基础知识)—— Executor框架及线程池

    在Java并发(基础知识)—— 创建.运行以及停止一个线程中讲解了两种创建线程的方式:直接继承Thread类以及实现Runnable接口并赋给Thread,这两种创建线程的方式在线程比较少的时候是没有 ...

  4. Java并发编程实践读书笔记(5) 线程池的使用

    Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到 ...

  5. 【java并发编程实战】第六章:线程池

    1.线程池 众所周知创建大量线程时代价是非常大的: - 线程的生命周期开销非常大:创建需要时间,导致延迟处理请求,jvm需要分配空间. - 资源消耗:线程需要占用空间,如果线程数大于可用的处理器数量, ...

  6. Java并发编程的艺术笔记(八)——线程池

    一.线程池的主要处理流程 ThreadPoolExecutor执行execute方法分下面4种情况. 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步需要获 ...

  7. 阿里架构师的这一份Spring boot使用心得:网友看到都收藏了

    阿里架构师的这一份Spring boot使用心得: 这一份PDF将从Spring Boot的出现开始讲起,到基本的环境搭建,进而对Spring的IOC及AOP进行详细讲解.以此作为理论基础,接着进行数 ...

  8. 深入理解Java并发框架AQS系列(一):线程

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...

  9. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

随机推荐

  1. POJ 2485 Highways 最小生成树 (Kruskal)

    Description The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has no public h ...

  2. Oracle OCP之硬解析在共享池中获取内存锁的过程

    转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/38684819 1.获得library cache Latch (1)在父游标的名柄没有找到 ...

  3. 【Linux命令】--(1)文件文件夹操作命令15条

    文件文件夹操作命令++++++++++++++++++++++++++++++++++++++++ 列出进入显示文件夹     ls cd pwd  创建移动删除文件     mkdir rm rmd ...

  4. Unity编程笔录--ulua+PureMVC框架简单热更新使用

    ulua+PureMVC框架简单热更新使用 前言: 1:作者官网论坛 首先介绍的是这个框架是一位大牛  骏擎[CP]  jarjin   写的,据说原本是"非常多人不知道怎么使用Ulua,所 ...

  5. 如何用分布式缓存服务实现Redis内存优化

    Redis是一种支持Key-Value等多种数据结构的存储系统,其数据特性是“ALL IN MEMORY”,因此优化内存十分重要.在对Redis进行内存优化时,先要掌握Redis内存存储的特性比如字符 ...

  6. 从头认识Spring-1.9 内部类注入Bean

    这一章节我们来讨论一下内部类注入Bean. 1.domain 蛋糕类:(跟前一章节的一样) package com.raylee.my_new_spring.my_new_spring.ch01.to ...

  7. 基于webrtc的媒体库測试代码以及接口介绍

    经过一段时间的项目验证,第一版接口能够定版了.满足一般的项目需求是没有问题了,接口非常清晰,凝视也写的非常清晰,大家有须要的就拿去測试吧,android版本号还在验证中.假设有什么问题或者须要源码.能 ...

  8. 操作系统的时区设置会影响数据库查询SYSDATE和SYSTIMESTAMP的值

    SYSDATE和SYSTIMESTAMP的值并不受数据库參数DBTIMEZONE的影响,操作系统时区的环境变量(如TZ)会影响它们的输入,由于SYSDATE和SYSTIMESTAMP实际是调用操作系统 ...

  9. 理解SetCapture、ReleaseCapture、GetCapture(控制了消息发往哪个窗口,即消息窗口)

    理解SetCapture.ReleaseCapture.GetCapture 正常情况下,鼠标指针位于哪个窗口区域内,鼠标消息就自动发给哪个窗口.如果调用了SetCapture,之后无论鼠标的位置在哪 ...

  10. P2657 [SCOI2009]windy数 数位dp

    数位dp之前完全没接触过,所以NOIP之前搞一下.数位dp就是一种dp,emm……用来求解区间[L,R]内满足某个性质的数的个数,且这个性质与数的大小无关. 在这道题中,dp[i][j]代表考虑了i位 ...