并发处理 是使得Amadahl定律代替摩尔定律成为计算机性能发展源动力的根本缘由;java
Amdahl定律 经过系统中串行化与并行化的比重来描述多处理器系统所能得到到的运算加速能力;缓存
摩尔定律 描述处理器晶体管数量与运行效率之间的发展关系;安全
计算机存储设备与处理器的运算速度存在几个数量级的差距,因此引入高速缓存Cache来做为内存与处理器之间的缓冲:即将运算所需用到的数据复制到Cache中,当运算结束后再同步回内存;多线程
缓存一致性:在多处理器系统中,每一个处理器都有本身的Cache,而它们又共享一个主内存,因此当多个处理器的运算任务都涉及到同一块内存区域时,将致使各自的缓存数据不一致;并发
内存模型:在特定的操做协议下,对特定的内存或高速缓存进行读写访问的过程抽象;函数
乱序执行优化:为了使得处理器内部的运算单元可以充分被利用,处理器会对输入代码进行乱序执行优化,并在运算以后将执行结果重组,可以保证计算结果正确但不保证输入代码各语句计算的前后顺序;性能
Java虚拟机规范中试图定义一种Java内存模型来屏蔽掉各类硬件与操做系统的内存访问差别,以实现让Java程序在各平台下都能达到一致的内存访问效果;优化
Java内存模型的主要目标是定义程序各个变量的访问细节,即虚拟机中将变量存储到内存和从内存取出这样的底层细节;编码
Java内存模型规定全部的变量都存储在主内存(虚拟机内存的一部分),每条线程有本身的工做内存(保存了该线程使用到的变量主内存副本拷贝),线程对变量的读写操做只限制在工做内存;操作系统
不一样线程也不能直接访问到对方工做内存中的变量,必须经过主内存进行传递;
Lock: 用于主内存的变量,将一个变量标识为一条线程独占的状态;
unlock: 做用于主内存的变量,释放后的变量可被其余线程锁定;
read: 做用于主内存的变量,将变量的值从主内存传输到线程工做内存;
load: 做用于工做内存的变量,它将read到的值放入工做内存的变量副本中;
use: 做用于工做内存变量,它把工做内存中的一个变量值传递到执行引擎,当虚拟机须要使用到变量的值的字节码指令时将会执行这个操做;
assign: 做用于工做内存变量,将从执行引擎收到的值赋值给工做内存的变量,当虚拟机遇到一个给变量赋值的字节码指令时执行这个操做;
store: 做用于工做内存变量,它把工做内存中一个变量的值传递到主内存中;
write: 做用于主内存变量,它将store的变量值放入到主内存变量中;
Java内存模型只要求read-load / store-write 操做必须按顺序执行,并无保证是连续执行,即read-load/ store-write 之间能够插入其余指令;
可见性 保证此变量对全部线程的可见性,基于volatile的运算在并发下并不是安全,由于Java内的操做并不是原子操做;
禁止指令重排 设置了内存屏障,不能将后面的指令重排序到内存屏障以前的位置;
volitale变量的读操做性消耗与普通变量几乎没有区别,可是写操做会慢一些,由于它须要在代码中插入许多内存屏障指令保证处理器不发生乱序执行;
内存间交互操做八个动做均为原子性,其中虚拟机提供了monitorenter、monitorexit来隐式使用lock、unlock,反映到Java代码中就是同步块 synchronized关键字,即在synchronized中的操做也具有原子性;
可见性指的是当一个线程改变了共享变量的值,其余线程可以当即得知这个修改;
除了volatile,synchronized、final也能实现可见性;
Java语言提供了volatile和synchronized关键字来保证线程之间操做的有序性;
指的是Java内存模型中定义的两项操做之间的偏序关系;
如下为Java内存模型中的“自然”先行发生关系,无需任何同步器协助,可在编码中直接使用;
程序次序规则:在一个线程内,按照程序流顺序,在前的操做先行于在后的操做;
管程锁定规则:一个unlock操做先行发生于后面对同一个锁的lock操做;
volatile变量规则:对一个volatile变量的写操做先行于后面对该变量的读操做;
线程启动规则:Thread对象的start()方法先行发生于此线程的每个动做;
线程终止规则:线程中的全部操做都先行发生于此线程的终止检测,可经过Thread.join()/isAlive()检测线程已经终止执行;
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可经过Thread.interrupted()方法检测到是否有中断发生;
对象终结规则:一个对象的初始化完成先行于它的finalize()方法的开始;
传递性:先行传递 A->B->C;
线程是CPU调度的基本单位;
Java Thread类与大部分的Java API有着显著的差异,它的全部关键方法都是声明为Native;
Native方法 意味着这个方法没有使用或没法使用平台无关的手段来实现;
线程实现主要有三种方式:
使用内核线程实现
内核线程就是直接由操做系统内核支持的线程,这种线程由内核来完成线程切换,内核经过操做调度器对线程进行调度,并负者将线程的任务映射到各个处理器上;
每一个内核线程能够视为内核的一个分身,这样操做系统就有能力处理多件事情,支持多线程的内核就叫作多线程内核;
程序通常不会直接去使用内核线程,而是去使用内核线程的一种高级接口--轻量级进程,轻量级进程就是咱们一般说的的线程,因为每一个轻量级进程都有一个内核线程支持;
轻量级进程局限性
因为基于内核线程实现,因此各类线程操做,如建立、析构、同步,都须要进行系统调用,系统调用的代价性对较高,须要在用户态-内核态来回切换;
每一个轻量级进程都须要一个内核线程的支持,所以轻量级进程须要消耗必定的内核资源,即一个系统支持轻量级进程的数量是有限的;
使用用户线程实现
一个线程只要不是内核线程,就能够认为是用户线程;
用户线程的创建、同步、销毁、调度彻底在用户态中完成,不须要内核的帮助;
若是程序实现得当,这种线程不须要切换到内核态,所以操做能够是很是快速且低消耗的,也能够支持规模更大的线程数量;
使用用户线程&轻量级进程混合实现
略;
协同式调度 线程执行的时间由自己控制,在执行完以后再通知系统切换到另外一个线程上,实现简单,没有线程同步问题;
抢占式调度 每一个线程将由系统来分配执行时间,线程的切换不禁线程自己来决定,线程的执行时间是系统可控的,也不会有一个线程致使整个进程阻塞的问题,Java使用的线程调度方式就是抢占式调度;
线程优先级 当多个线程同时处于Ready状态时,优先级越高的线程就越容易被系统选择执行;
在任意一个时间点,一个线程只能有其中的一种状态
新建:建立后还没有启动的线程处于这种状态;
运行:Runnable包括了操做系统线程状态中的Running/Ready,也就是处于此状态线的线程可能正在执行/有可能正在等待CPU分配执行时间;
无限期等待:处于这种状态的线程不会被分配CPU执行时间,他们要等待被其余线程显示唤醒;
如下方法会让线程进入无限期等待状态:
未设置参数的Object.wait();
未设置参数的Thread.join();
LockSupport.park();
限期等待:处于这种状态的线程也不会被分配CPU执行时间,不过无需等待被其余线程显示唤醒,在必定时间以后他们会由系统自动唤醒;
如下方法会让线程进入限期等待状态:
Thread.sleep();
设置了Timeout参数的Object.wait();
设置了Timeout参数的Thread.join();
LockSupport.parkNanos();
LockSupport.parkUntil();
阻塞:阻塞状态 与 等待状态的区别是,阻塞状态在等待着获取到一个排他锁,这个事件将在另一线程放弃这个锁的时候发生,而等待状态就是则是在等待一段时间或者唤醒动做的发生;
结束:已终止线程的线程状态,线程已经结束执行;
当多个线程访问一个对象时,若是不考虑这些线程在运行时环境下的调度和交替执行,也不须要进行额外的同步,或者在调用方法进行任何其余的协调操做,调用这个对象的行为均可以得到正确的结果,则这个对象是线程安全的;
按照线程安全的“安全程度”由强至弱排序,可将Java语言中各类操做共享的数据分为如下五类:
不可变:
在Java语言中,不可变的对象必定是线程安全的,final关键字,只要一个不可变的对象正确地被构建出来。那其外部的可见状态就不会改变;
String类对象是一个典型的不可变对象,咱们调用它的substring()/replace()/concat()这些方法都不会影响到它原来的值,只会返回一个新构造的字符串对象;
Long/Double/枚举类型、Number部分子类
AtomicInteger/AtomicLong则并不是不可变 ?
绝对线程安全
Java API中标注本身是线程安全的类,大多数都不是绝对的线程安全;
相对线程安全
Vector/HashTable/Collections/synchronizedCollection()等;
线程兼容:
线程兼容指的是对象自己并非线程安全的可是能够经过调用端正确地使用同步手段来保证对象在并发环境中能够安全的使用;
Java ApI中大部分的类都是属于线程兼容的,如以前定的Vector和HashTable相对应的集合类,ArrayList,HashMap类等;
线程对立
指的是调用端是否采用了同步措施,都没法在多线程环境中并发使用的代码;
一个线程对立的例子是Thread类的suspend()和resume()方法;
互斥同步
同步指的是多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用;
互斥是实现同步的一种手段,临界区、互斥量、信号量都是主要的互斥实现方式;
synchronized是java语言中重量级的操做
java.util.concurrent中的重入锁ReentrantLock,表现为原生语法上的互斥锁:
等待可中断 持有锁的线程长期不释放锁的时候,正在等待的线程能够选择放弃等待,改成处理其余事情;
公平锁 指的多个线程在等待同一个锁是,必须按照申请锁的时间顺序来得到锁,synchronized是公平锁,ReentrantLock默认状况是非公平的,但能够经过带布尔值的构造函数是用公平锁;
锁绑定多个条件 略;
互斥同步属于一种悲观并发策略,认为只要不去作正确的同步措施,就确定会出现问题;
非阻塞同步 基于冲突检测的乐观并发策略,即先进性操做,若是没有其余线程争用则操做成功,若是产生冲突则再采起补偿措施,无需挂起线程;
自旋锁
共享数据的锁定状态只会持续很短的一个时间,在这段时间去挂起和恢复锁并不值得,因此只须要让线程执行一个忙循环;
自适应自旋锁
着自旋的时间不在固定了,而是由前一次在同一个锁上的自旋时间以及锁定拥有者状态来决定;
锁消除
锁消除指的是虚拟机在即时编译器运行时,对一些代码上要求同步,可是被检测到不可能存在共享数据竞争的锁进行消除;
锁粗化
若虚拟机探测到有一连串的操做都对同一个对象加锁,则会将锁同步的范围扩大(粗化)到整个操做徐磊的外部;
轻量级锁
HotSpot虚拟机的对象头分为两部分信息
第一部分用于存储对象自身的运行时数据,如哈希码,GC分代年龄,这部分数据成为Mark Word,它是实现轻量级锁和偏向锁的关键;
另外一部分用于存储指向方法区对象类型数据的指针;
在代码进入同步块的时候,若是此同步对象未被锁定,虚拟机首先将在当前线程的栈帧中创建一个名为锁记录的空间,用于存储Mark Word拷贝;
而后虚拟机将使用CAS操做将对象的Mark Word更新为指向Lock Record的指针,若是这个更新动做成功,则该线程拥有了该对象的锁,而且对象Mark Word的锁标志位将转变为“00”,即表示此对象处于轻量级锁状态;
偏向锁
目的是消除数据在无竞争状况下的同步原语,进一步提升程序的运行性能;
偏向锁会偏向于第一个得到它的进程,若是在接下来的执行过程当中,该锁没有被其余的线程获取,则持有偏向锁的线程将永远不须要进行同步;
。。。
就先到这了,建议仍是去看原文《深刻理解Java虚拟机》