并发指在宏观上的同一时间内同时执行多个任务。为了知足这一需求,现代的操做系统都抽象出 线程 的概念,供上层应用使用。java
这篇博文不打算详细展开分析,而是对java并发中的概念和工具作一个梳理。
沿着并发模型、并发要解决的问题、基本工具、衍生工具这一思路展开。程序员
<!-- more -->算法
首先线程是什么?线程是由OS抽象并实现的,咱们知道OS的职责是管理并合理分配硬件资源,那么OS为了更好的管理、分配CPU资源,同时也为了知足同时执行任务这一需求,设计了线程这一律念。编程
虽然java程序运行在JVM虚拟机上,可是java的线程仍然是对操做系统原生线程的封装,同时,jvm对线程实现时也将jvm的运行栈设计成线程私有内存,所以,java线程和原生线程在理解上实际上没太大区别。缓存
线程的五种状态:安全
graph LR 新建 --> 就绪; 就绪 --> 运行; 运行 --> 就绪; 运行 --> 阻塞; 阻塞 --> 就绪; 运行 --> 死亡;
先来看上面的就绪状态和运行状态。咱们知道线程虽然宏观上是同时执行的,可是微观上使用如时间片轮转算法使得线程依次执行。那么,同一时间只有一个线程执行,其它须要执行的线程处于 就绪队列 中,等待本身被调度到。数据结构
而若是线程想要暂时放弃在CPU上运行的权利,就会阻塞本身。这时对应着阻塞状态,同时线程会从就绪队列中移除,进入等待队列。
很显然,阻塞线程被唤醒确定是进入就绪队列等待调度,而不多是直接分配到CPU上运行。多线程
在线程同步时,线程可能因为如下状况被阻塞:并发
线程同步面临两个问题,想象下有两个线程在协做工做完成某项任务。那么须要解决如下问题:jvm
在多线程程序的性能问题上,若是是对于一样一段临界区的多线程访问,那么则有如下几个思路:
以上三种思路的性能优劣没有一个普适的结果,和具体的场景相关。
并发中还会出现如下几种状况致使系统不可用:
并发编程中须要考虑的几个概念:
从我我的的理解来看,原子性属于由并发和线程这一理论概念天然而然推导衍生而来的概念,而可见性和有序性是具体的工程实践中产生的。
实际中,jvm并不能实现的特别完美,总会有工程上的妥协。理论模型与实际模型没法完美契合,总存在必定的误差。
好比说,jvm为了向性能妥协使用了缓存机制,牺牲了数据一致性,这就产生了可见性的概念,须要程序员编程时本身控制。
jvm为了指令更高效率的执行进行了指令重排优化,则产生了有序性的问题。印象里之前大学里学过的CPU的流水线技术,为了指令可以更好的被CPU流水线利用,减小流水线的空闲时间,编译器编译时也会在不影响 串行语义 的前提下,进行指令重排。
总而言之,这是在性能和理论模型完整性之间的一种妥协。
技术上的工具、概念繁多复杂,可是若是咱们能理解技术设计上无时无刻的不运用抽象和分层的手段,
那么,咱们能够把技术上的工具分为两种:
更高层次的工具对基础工具进行了抽象和封装,屏蔽了其中的实现细节。
这里想强调的是,工具的接口和实现是分开的,二者能够没有关系。
如java的监视器锁从接口上来看,其语义和互斥锁同样。然而它并不必定使用互斥锁实现,而是能够为了性能存在优化,只要最终的行为与接口相同便可。
有三种用于线程同步的工具:
锁。锁可用于规定一个 临界区,同一时间临界区内仅能由一个线程访问。其余线程则在临界区外等待(阻塞)。
在java中,Object类有wait()、notify()和notifyAll()之类的方法。
这些方法能够认为每一个对象都内置了一个条件变量,而这些方法是对这些条件变量的操做,所以,可使用这些方法将对象看成条件变量使用,从而作到线程的同步。
底层机制的特色直接获得的:
1. java中的volatile关键字。 2. CAS。
volatile关键字可以保证变量的可见性,或者说是读或写的原子性。
CAS即compareAndSwap,原子操做 。
CAS操做直接可以对应到单条CPU指令,所以自然具备原子性。java中是经过JNI调用C语言从而调用CPU底层指令实现。
CAS的行为和如下代码一致:
int cas(long *addr, long old, long new) { if (*addr == old) { *addr = new; return 1; } else { return 0; //* } }
那么CAS能够作什么呢?不少乐观并发控制能够基于CAS实现。
好比说,经过一个标记变量来记录临界区被谁占有,线程进入临界区前不断的使用CAS操做判断标记变量是否为空同时将其记录为本身,来实现锁机制。这就是自旋锁的思路。
除此以外,乐观锁也能用CAS实现,好比java的Atomic系列,就是这样实现的。
前面说到能够认为每一个对象内置一个条件变量,一样,每一个对象也内置一个锁。这个内置锁在Java中被抽象为监视器锁(monitor)。
synchronized关键字的使用实际上就至关于使用监视器锁定义了一个临界区。使用这种语法也特别直观简单,因此java常常用synchronizd来进行线程的同步。
JDK1.6以后,为了提高监视器锁的性能,java经过某些手段进行了优化。其中包含锁优化机制,对应三种锁:
1. 偏向锁 2. 轻量级锁 3. 重量级锁
一开始只有一个线程使用线程时使用偏向锁,当存在多个线程使用时膨胀为轻量级锁,而出现比较多的线程竞争时再膨胀为重量级锁。
Vector
、 ConcurrentHashMap
等。ReentrantReadWriteLock
。其它:
双重检查锁
为了表达某种锁的特色,也会有着不少的概念。
可是这种概念对应的不是某一种锁,而是一类拥有特定属性的锁。如: