volatile,指令重排和多线程简单秒杀(五)

简介: volatile,指令重排和多线程简单秒杀(五)

一. 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;
    }
}


运行程序,查看控制台:


20200620081838224.png


会一直运行下去。 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;
    }
}


运行程序,查看控制台打印输出:


20200620081843790.png


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.


然而,运行程序,查看控制台(可能需要多次运行,才会可能出现指令重排的效果):


20200620081851743.png


会发现,造成了指令重排。


一.二.二 使用 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);
    }
}


运行程序,查看控制台输出:


20200620081857731.png


二. 多线程秒杀


关于秒杀 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();
        }
    }
}


运行程序,查看控制台输出


20200620081903266.png


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

相关文章
|
9月前
|
缓存 人工智能 安全
4. Java 的线程安全机制之`volatile`
4. Java 的线程安全机制之`volatile`
|
1月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
79 3
|
28天前
|
安全 Java 编译器
Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字(一)
线程安全问题是多线程编程中最典型的一类问题之一。如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的。 通俗来说,线程不安全指的就是某一代码在多线程环境下执行会出现bug,而在单线程环境下执行就不会。线程安全问题本质上是由于线程之间的调度顺序的不确定性,正是这样的不确定性,给我们的代码带来了很多“变数”。 本文将对Java多线程编程中,线程安全问题展开详细的讲解。
42 0
|
29天前
|
Java 编译器
多线程(volatile)
多线程(volatile)
17 0
|
11月前
|
安全 Java
Java线程安全:同步方法、同步代码块、volatile 变量和原子变量
Java线程安全:同步方法、同步代码块、volatile 变量和原子变量
206 1
|
1月前
|
存储 缓存 Java
volatile 与线程的那些事
volatile 与线程的那些事
31 0
|
1月前
|
设计模式 安全 编译器
线程学习(3)-volatile关键字,wait/notify的使用
线程学习(3)-volatile关键字,wait/notify的使用
36 0
|
1月前
|
缓存 人工智能 安全
4. Java 的线程安全机制之`volatile`
4. Java 的线程安全机制之`volatile`
|
1月前
|
Java 编译器
Java多线程:什么是volatile关键字?
Java多线程:什么是volatile关键字?
38 0
|
1月前
|
存储 安全 Java
关于volatile解决内存可见性问题(保证线程安全)
关于volatile解决内存可见性问题(保证线程安全)