并发编程的核心是为了提升电脑资源的利用率,由于现代操做系统都是多核的,能够同时跑多个线程。那么是否是线程越多越好? 因为线程的切换涉及上下文的切换,所谓上下文就是线程运行时须要的资源,系统要分配给它消耗时间。因此为了减小上下文的切换,咱们有如下几种方法:java
协程和线程区别:每一个线程OS会给它分配固定大小的内存(通常2MB)来存储当前调用或挂起的函数的内部变量,固定大小的栈意味着内存利用率很低或有时面对复杂函数没法知足要求,协成就实现了可动态伸缩的栈(最小2KB,最大1GB).其二OS线程受操做系统调度,调度时要将当前线程状态存到内存,将另外一个线程执行指令放到寄存器,这几步很耗时。Go调度器并不是硬件调度器,而是Go语言内置的一中机制,所以goroutine调度时则不须要切换上下文。
Java并发机制的底层实现原理,java代码编译成字节码后加载到JVM中,JVM执行字节码最终转化成汇编命令在CPU上运行,所以Java所使用的并发机制依赖JVM的实现和CPU指令。Java大部分并发容器和框架都依赖于volatile和原子操做的实现原理。算法
Java之间经过共享内存进行通讯,处理器和编译器为了提升性能会对指令进行重排序,这在单线程状况下不会发生异常,可是在多线程下就会形成结果的不一致编程
int a=0; public int calculate(){ a=1; 1 boolean flag=true; 2 if(flag){ return a*a; } return 0; }
现有两个线程执行这段代码,线程A执行时对指令进行了重排序先制行 2 在执行 1,在中间线程B插入了进来此时a=1值还没被写入致使返回结果为0发生错误。缓存
处理器遵循as-if-serial语义,即无论如何重排序结果不变,可是多线程状况下会出现错误
为了不重排序,Java引入了volatile变量,使得语句在操做被volatile修饰的变量时禁止指令重排序。在执行指令时插入内存屏障也就是这个目的,最关键的是volatile的读/写内存语义以下服务器
volatile的缺陷在于改这个动做是不彻底的,所以又提出了CAS机制,CAS会使用处理器提供的机器级别的原子命令(CMPXCHG),原子执行读-改-写操做。Java concurrent包中一个通用化的实现模式就是结合二者,步骤以下多线程
线程池的核心做用就是维护固定的几个线程,有任务来的时候直接使用避免建立/销毁线程致使的额外开销。 线程池执行流程以下:架构
提交任务-->核心线程池已满? 是 提交任务到消息队列--->队列已满? 是 按指定策略执行 否 建立线程执行任务 否 加进队列
了解了线程池的原理最重要的就是如何是去使用它,而使用的关键就是参数的设置。并发
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
以上是ThreadPoolExecutor的构造函数,咱们逐一看一看各参数的含义框架
提交线程池有两个方法,一个是submit这个不须要返回值,一个是submit会返回一个future对象,并经过future的get()方法获取返回值(该方法会阻塞直到线程完成任务)。函数
合理配置线程池,CPU密集型任务配置少数线程池如N(CPU个数)+1,I/O密集型任务配置多一点的线程池如2N(CPU个数),其次是使用有界队列即便发现错误。
在HotSpot VM的线程模型中,Java线程被一对一的映射成本地操做系统的线程,操做系统会调度线程把它们分配给可用的CPU。在上层Java经过用户级调度器Executor将任务映射为几个线程,在下层操做系统内核将这些线程映射到硬件处理器上面。
Executor的出现将任务与如何执行任务分离开了,避免了每建立一个线程就要执行它。Executor的整个架构有一下几个要点
Executor最核心的就是ThreadPoolExecutor,下面介绍如下以及各自使用场景