线程的使用带来了很是多的便利同时,也给咱们带来了不少困扰java
当多个线程访问某个对象时,无论运行时环境采用何种调度方式或者这些线程将如何交替执行,而且在主调代码中不须要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的数组
一、原子性缓存
二、可见性安全
三、有序性架构
一、CPU 增长了告诉缓存,均衡与内存的速度差别app
二、操做系统增长进程、线程、以及分时复用 CPU,均衡 CPU 与 I/O 设备的速度差别函数
三、编译程序优化指令的执行顺序,使得可以更加合理的利用缓存工具
public class AtomicDemo{ public static int count=0; public static void incr(){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } count++; //count++ (只会由一个线程来执行) } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1000; i++) { new Thread(AtomicDemo::incr).start(); } Thread.sleep(4000); System.out.println("result:"+count); } }
JMM优化
Java内存模型是一种抽象结构,它提供了合理的禁用缓存以及禁止重排序的方法来解决可见性,有序性问题this
一、Volatile、synchronized、final 关键字
二、Happens-Before 原则
锁的范围
一、对于普通同步方法,锁是当前实例对象。
二、对于静态同步方法,锁是当前类的Class对象。
三、对于同步方法块,锁是 Synchonized 括号里配置的对象。
//对象锁(同一对象生效),锁的是this public class SyncDemo { //对象锁 public synchronized void demo(){} public static void main(String[] args) { SyncDemo syncDemo1 = new SyncDemo(); SyncDemo syncDemo2 = new SyncDemo(); //没法实现两个线程的互斥 new Thread(()->{ syncDemo.demo(); }).start(); new Thread(()->{ // syncDemo1.demo(); syncDemo.demo();//BLOCKED状态 }).start(); } }
//静态同步方法(类级别的锁),锁的是 SyncDemo.class public class SyncDemo { //静态同步方法, public synchronized static void demo(){ } public static void main(String[] args) { SyncDemo syncDemo = new SyncDemo(); SyncDemo syncDemo1 = new SyncDemo(); //若是synchronized为静态方法,那么下面两个线程会互存在斥 new Thread(()->{ syncDemo.demo(); }).start(); new Thread(()->{ syncDemo1.demo(); }).start(); } }
//同步方法块,能够是对象锁,也能够是类级别锁,可是范围是可控的。 public class SyncDemo { public void demo(){ //TODO synchronized (this){//对象锁 } //TODO } public void demo1(){ //TODO synchronized (SyncDemo.class){//类级别的锁 } //TODO } }
一、以上出现的锁就只有两把,一个是对象锁,一个是类级别锁。
二、synchronized方法块中锁的范围是可控的
synchronized 的优化
一、自适应自旋锁
二、引入偏向锁、轻量级锁
三、锁消除、锁粗化
volatile 是能够用来解决可见性和有序性问题的
Lock指令的做用
一、将当前处理器缓存行的数据写回到系统内存
二、这个写回内存的操做会使在其余CPU里缓存了该内存地址的数据无效
什么状况下须要用到volatile
当存在多个线程对同一个共享变量进行修改的时候,须要增长volatile,保证数据修改的实时可见
public class VisableDemo { //volatile解决[可见性] public volatile static boolean stop=false; public static void main(String[] args) throws InterruptedException { Thread thread=new Thread(()->{ int i=0; while(!stop){ i++; } System.out.println("result:"+i); }); thread.start(); System.out.println("begin start thread"); Thread.sleep(1000); stop=true; //主线程中修改stop的值 } }
//volatile 解决 [有序性] value = 3; void exeToCPU0(){ value = 10; isFinsh = true; } void exeToCPU1(){ if(isFinsh){ assert value == 10; } } /* CPU层面的内存屏障,(X86架构里面提供了三种内存屏障) 一、Store Barrier[写],强制全部在 store 屏障指令以前的 store 指令,都在该 store 屏障指令执行以前被执行,并把 store 缓冲区的数据都刷到 CPU 缓存 二、Load Barrier[读],强制全部在 load 屏障指令以后的 load 指令,都在该 load 屏障指令执行以后被执行,而且一直等到 load 缓冲区被该 CPU 读完才能执行以后 load 指令 三、Full Barrier[全],复合了 load 和 store 屏障的功能 */ value = 3; void exeToCPU0(){ value = 10; storebarrier();//写屏障,让value 与 isFinsh 没法指令重排 isFinsh = true; } void exeToCPU1(){ if(isFinsh){ loadbarrier();//读屏障,确保value必定是10[最新值] assert value == 10; } }
本质上来讲:volatile 其实是 经过内存屏障来防止指令重排序 以及 禁止 cpu 高速缓存来解决可见性问题。
而 #Lock 指令,它本意上是禁止高速缓存解决可见性问题,但实际上在这里,它表示的是一种内存屏障的功能。也就是说针对当前的硬件环境, JMM 层面采用 Lock 指令做为内存屏障来解决可见性问题。
final 在 Java中是一个保留的关键字,能够声明成员变量、方法、类以及本地变量。一旦你将引用声明作 final,你将不能改变这个引用了
final域 与 线程安全 有什么关系?
对于 final 域,编译器 和 处理器 要遵照两个重排序规则
1)在构造函数内对一个 final 域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操做之间不能重排序。
2)初次读一个包含 final 域的对象的引用,与随后初次读这个final域,这两个操做之间不能重排序。
//写 final 域得重排序规则 public class FinalExample{ int i; //普通变量 final int j; //final变量 static FinalExample obj; public FinalExample(){ //构造函数 i = 1; //写普通域 j = 2; //写 final 域 } public static void writer(){//写线程 A 执行 obj = new FinalExample(); } public static void reader(){ //读线程 B 执行 FinalExample object = obj; //都对象引用 int a = object.i; //读普通域 int b = object.j; //读 fianl 域 } }
写final域重排序规则
一、JMM禁止编译器把final域的写重排序到构造函数以外。
二、编译器会在 final 域的写以后,构造函数 return 以前,插入一个 StoreStore 屏障。这个屏障禁止处理器把final域的写重排序到构造函数以外
读域的重排序规则
在一个线程中,初次读对象引用与初次读该对象包含的 final 域,JMM 禁止处理器重排序这两个操做,编译器会在读final域操做的前面插入一个 LoadLoad 屏障。
什么是Happens-Before?
Happens-Before 是一种可见性规则,它表达的含义是前面一个操做的结果对后续操做是可见的。
A Happens-Before B ➡ A 得执行结果对 B 可见
6种 Happens-Before 规则 [ 天生知足可见性规则 ]
一、程序顺序规则
二、监视器锁规则
三、Volatile 变量规则
四、传递性
五、start() 规则
六、Join() 规则
/* as-if-serial 单线程中无论怎么重排序,获得的执行结果是不能改变的 编译器/处理器都要遵照,所以不会对存在数据依赖关系的语句进行重排序 */ class VolatileExample{ int x = 0; volatile boolean v = false; public void write(){ x = 42; v = true; } public void reader(){ if(v == true){ //这里x会是多少? } } }
对一个锁的解锁 Happens-Before 于后续对这个锁的枷锁
synchronized(this){//此处自动枷锁 //x 是共享变量,初始值 = 10 if(this.x < 12){ this.x = 12; } }//此处自动解锁
对一个 volatile 域的写,Happens-Before 于任意后续对这个 volatile 域的读。
其实是经过内存屏障来实现的
若是 A happens-before B,且 B happens-before C,那么 A happens-before C
class VolatileExample{ int x = 0; volatile boolean v = false; public void write(){ x = 42; v = true; } public void reader(){ if(v == true){ //这里x会是多少? } } }
若是线程A执行操做ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操做happens-before于线程B中的任意操做
Thread B = new Thread(()->{ //主线程调用 B.start()以前 //全部对共享变量的修改,此处皆可见 //此例中,var == 77 }); //此处对共享变量 var 修改 var = 77; //主线程启动子线程 B.start();
若是线程A执行操做 ThreadB.join() 并成功返回,那么 线程B 中的任意操做 happens-before 于 线程A 从 ThreadB.join() 操做成功返回
Thread B = new Thread(()->{ //此处对共享变量var 修改 var = 66 }); //例如此处对共享变量修改 //则这个修改结果对线程B可见 //主线程启动子线程 B.start(); B.join(); //子线程全部对共享变量的修改 //在主线程调用B.join()以后皆可见 //此例中,var == 66
原子性问题的解决方案
synchronized、Lock
J.U.C 包下的 Atomic 类 [ 无锁工具的典范 ]
import java.util.concurrent.atomic.AtomicInteger; //atomic 保证原子性 public class AtomicDemo{ // public static int count=0; private static AtomicInteger atomicInteger=new AtomicInteger(0); public static void incr(){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // count++; //count++ (只会由一个线程来执行) atomicInteger.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 1000; i++) { new Thread(AtomicDemo::incr).start(); } Thread.sleep(4000); // System.out.println("result:"+count); System.out.println("result:"+atomicInteger.get()); } }
Atomic 实现原理
一、Unsafe 类
二、CAS
public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
Atomic 分类
一、原子更新基本类型
二、原子更新数组
三、原子更新引用类型
四、原子更新字段类
public class ThreadLocalDemo { private static Integer num=0; public static final ThreadLocal<Integer> local=new ThreadLocal<Integer>(){ protected Integer initialValue(){ return 0; //初始值 } }; public static final ThreadLocal<Integer> local1=new ThreadLocal<Integer>(); public static void main(String[] args) { Thread[] threads=new Thread[5]; //但愿每一个线程都拿到的是0 for (int i = 0; i < 5; i++) { threads[i]=new Thread(()->{ // num+=5; int num=local.get(); //拿到初始值 local1.get(); num+=5; local.set(num); System.out.println(Thread.currentThread().getName()+"->"+num); },"Thread-"+i); } for(Thread thread:threads){ thread.start(); } } }
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
发布的意思是使一个对象可以被当前范围以外的代码所使用
public static HashSet<Person> persons; public void init(){ persons = new HashSet<Person>(); }
//不安全发布 private String[] states = {'a','b','c','d'}; //发布出去一个 public String[] getStates(){ return states; } public static void main(String[] args){ App unSafePub = new App(); System.out.println("Init array is: "+Arrays.toString(unSafePub.getStates())); unSafePub.getStates()[0] = "Mic!"; System.out.println("After modify.the array is: "+ Arrays.toString(unSafePub.getStates())); }
也成为对象的逃逸,一种错误的发布,当一个对象尚未构造完成时,就使它被其余线程所见
一、在静态初始化函数中初始化一个对象引用
二、将对象的引用保存到volatile类型的域或者AtomicReference对象中(利用volatile happen-before规则)
三、将对象的引用保存到某个正确构造对象的final类型域中(初始化安全性)
四、将对象的引用保存到一个由锁保护的域中(读写都上锁)
//静态初始化 public class StaticDemo { private StaticDemo(){} private static StaticDemo instance=new StaticDemo(); public static StaticDemo getInstance(){ return instance; } }
//final域 public class FinalDemo { private final Map<String,String> states; public FinalDemo(){ states=new HashMap<>(); states.put("mic","mic"); } }
//volatile public class VolatileSyncDemo { private VolatileSyncDemo(){} //DCL问题[加 volatile] private volatile static VolatileSyncDemo instance=null; public static VolatileSyncDemo getInstance(){ if(instance==null){ synchronized(VolatileSyncDemo.class) { if(instance==null) { instance = new VolatileSyncDemo(); } } } return instance; } /** * instance = new VolatileSyncDemo(); * -> * 1. memory=allocate() * 2. * 3. instance=memory * ctorInstance(memory) * * 1.3.2 (不完整实例) */ }