1. 内存模型概念多线程
(1)内存模型(Java Memory Model)和内存结构(堆栈那些)不是一个层面的概念,JMM 定义了一套在多线程读写共享数据(成员变量,静态变量等,而不是局部变量这种线程私有的)时,对数据的可见性、有序性、和原子性的规则和保障。并发
(2)JMM规定了全部的变量都存储在主内存(虚拟机内存的一部分,但能够和操做系统的类比)中;ide
每条线程还有本身的工做内存,线程的工做内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的全部操做(读取,赋值)都必须在工做内存中进行,而不能直接读写主内存中的变量;优化
不一样的线程之间也没法直接访问对方的工做内存中的变量,线程间变量的值传递均须要经过主内存来完成。操作系统
(3)若是非要把主内存工做内存,和内存结构的堆栈方法区对应,那么主内存应该对应堆中的对象的实例数据部分,而工做内存对应栈。线程
2. 原子性对象
要执行就执行完,不能执行一半blog
违背原子性例子:两个线程对一个静态变量 i=0 执行 i++ 和 i--(每一个对应四条JVM字节码指令),可能致使结果不是0内存
解决:用synchronized加锁,加锁位置应尽可能减小代码获取释放锁的次数编译器
synchronized( 对象 ) { 要做为原子操做代码 }
3. 可见性
指一个对象能看到或访问另外一个对象的能力
违背可见性的例子:t线程看不到主线程run变量的改变
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{while(run){// .... } }); t.start(); Thread.sleep(1000); run = false; // 线程t不会如预想的停下来}
解决:volatile(易变关键字),它能够用来修饰成员变量和静态成员变量,线程操做 volatile 变量都是直接操做主存
所以上面的run被修改后会存到主内存,t线程访问主内存内容会看到改变
4. 有序性
代码按顺序执行
违背有序性的例子:因为即时编译器在运行时会有指令重排的优化,多线程状况下可能出现非预期结果
解决:volatile 修饰的变量,能够禁用指令重排
所以volatile能够保证可见性和有序性,不能保证原子性,但属于轻量级并发控制;而synchronized能够保证三者,但更重量级
5. CAS
CAS 即 Compare and Swap ,它的实现用的是乐观锁的思想
// 须要不断尝试while(true) {int 旧值 = 共享变量 ; // 好比拿到了当前值 0int 结果 = 旧值 + 1; // 在旧值 0 的基础上增长 1 ,正确结果是 1if( compareAndSwap ( 旧值, 结果 )) {// 成功,退出循环 } }