一. volatile 关键字
关于 volatile关键字,大家可以看 Matrix海子 前辈的文章: Java并发编程:volatile关键字解析
volatile 可以解决两个问题:
1.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2.禁止进行指令重排序。
一.一 不同线程立即可见
一.一.一 不使用 volatile 关键字时
public class VolidateTest { private static int a=0; public static void main(String[] args) throws Exception { new Thread(()->{ while(a==0){ } }).start(); Thread.sleep(1000); //改变值 a=1; } }
运行程序,查看控制台:
会一直运行下去。 main线程改变了 a的值, 但是 Thread-0 线程并不能立即检测到该值变化,还是用以前的 a=0, 导致一直while 死循环下去。
一.一.二 使用 volatile 关键字时
public class VolidateTest { //改变之后,对其他线程立即可见。 private static volatile int a=0; public static void main(String[] args) throws Exception { new Thread(()->{ //1s之后,检测到发生改变,会立即停止运行。 while(a==0){ } }).start(); Thread.sleep(1000); //改变值 a=1; } }
运行程序,查看控制台打印输出:
1s 钟之后,会停止运行。 Thread-0线程能够检测到 a的值发生了变化。
一.一.三 单例模式使用 volatile 关键字
单例模式时, 也常常使用这个关键字 volatile.
public class DoubleCheckLock { //立即检测到变化 private static volatile DoubleCheckLock instance; private DoubleCheckLock(){ } public static DoubleCheckLock getInstance(){ //双重多例时 if(null==instance){ synchronized (DoubleCheckLock.class){ if(null==instance){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } instance=new DoubleCheckLock(); } } } return instance; } public static void main(String[] args) { Thread t = new Thread(()->{ System.out.println(DoubleCheckLock.getInstance()); }) ; t.start(); System.out.println(DoubleCheckLock.getInstance()); } }
一.二 禁止指令重排
一.二.一 不使用 volatile 关键字时
public class HappenBefore { private static int a=0; private static boolean flag=false; public static void main(String[] args) throws Exception{ for(int i=0;i<10;i++){ //重置 a=0; flag=false; //线程1 运行时,设置改变值 Thread t1= new Thread(()->{ a=1; flag=true; }); //线程2 对结果判断。 当flag=true时, a=1*1=1 Thread t2= new Thread(()->{ if(a==0){ System.out.println("输出值:flag:"+flag+",a:"+a); } }); t1.start(); t2.start(); } Thread.sleep(3000); } }
以前的理解, a=0时才输出, 输出的值只能是 flag为false, a=0.
然而,运行程序,查看控制台(可能需要多次运行,才会可能出现指令重排的效果):
会发现,造成了指令重排。
一.二.二 使用 volatile 关键字时
public class HappenBefore { //使用 volatile 禁止指令重排 private static volatile int a=0; private static volatile boolean flag=false; public static void main(String[] args) throws Exception{ for(int i=0;i<10;i++){ //重置 a=0; flag=false; //线程1 运行时,设置改变值 Thread t1= new Thread(()->{ a=1; flag=true; }); //线程2 对结果判断。 当flag=true时, a=1*1=1 Thread t2= new Thread(()->{ if(a==0){ System.out.println("输出值:flag:"+flag+",a:"+a); } }); t1.start(); t2.start(); } Thread.sleep(3000); } }
运行程序,查看控制台输出:
二. 多线程秒杀
关于秒杀 CAS,后面老蝴蝶会讲解。 现在,只理解这个模型即可。
关于秒杀 CAS, 可以看 Andy.Zhou 前辈的文件: 秒杀系统架构分析与实战
写一个小例子,看一下。
二.一 秒杀小例子
public class CASTest { //原子性int private static AtomicInteger stoge=new AtomicInteger(5); public static void main(String[] args) { for(int i=0;i<10;i++){ new Thread(()->{ //休眠一下,进行网络延迟 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //减去,并且获取 int left=stoge.decrementAndGet(); if(left<0){ System.out.println("商品没有了,被抢光了,"+Thread.currentThread().getName()+"没有抢到"); return ; } System.out.println("人:"+Thread.currentThread().getName()+"抢得了第: "+(left+1)+"件商品"); }).start(); } } }
运行程序,查看控制台输出
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!