volatile关键字简介

项目摘要:

 (略)

  • 项目具体实施:
  1. volatile关键字简介

功能关键点: 变量的可见性,禁止变量重排序,不保证原子性

  1. volatile与synchronized的区别:
  1. volatile只能修饰实例变量和类变量,而synchronized可以修饰方法和代码块。
  2. volatile保证数据的可见性,但是不保证原子性(多线程进行写操作,不保证线程安全);而synchronized 是一种排他(互斥)的机制。
  3. volatile用于禁止重排序,可以解决单例双重检查对象初始化代码执行乱序问题。
  4. volatile可以看做是轻量版的synchronized不保证原子性,但是如果对一个共享变量进行多个线程的赋值,而没有其他的操作,那么就可以用volatile代替synchronized,因为赋值本身是有原子性的,而volatile又保证了可见性,所以就可以保证线程安全了。
  5. volatile不会造成线程阻塞,而synchronized会造成线程阻塞。
  6. volatile标记的变量不会被编译器优化,synchronized标记的变量可以被编译器优化。
  1. 实例1:线程之间的不可见性,该程序中flag值已经被修改,但是对于主线程并不可见。

package volatiledemo;

/**

 * 线程之间的数据的不可见

 */

public class VolatileDemo01 {

   private static boolean flag = false;

 

   public static void main(String[] args) throws InterruptedException {

      Thread t1 = new Thread(new Runnable() {

         @Override

         public void run() {

            try {

               Thread.sleep(10);

            } catch (InterruptedException e) {

               e.printStackTrace();

            }

            flag = true;

            System.out.println("flag:" + flag);

         }

      });

      t1.start();

      while (true) {

         if (flag) {

            System.out.println("主线程进入循环运行");

         }

      }

   }

}

  1. 实例2:利用volatile关键字解决不可见问题,代码如下:

private static volatile boolean flag = false;

  1. 实例3:不保证原子性:该程序最终结果不能到达一百万总和。

package volatiledemo;

public class VolatileDemo03 {

   private static int count = 0;

   public static void main(String[] args) throws InterruptedException {

      for (int i = 0; i < 100; i++) {

         new Thread(new Runnable() {

            @Override

            public void run() {

               for (int i = 0; i < 10000; i++) {

                  count++;

                  System.out.println(Thread.currentThread().getName() + "  count=======>" + count);

               }

            }

         }, "" + i + "个线程").start();

      }

   }

}

  1. 实例4:加上volatile并不能解决该问题

private static volatile int count = 0;

  1. 实例5:利用synchronized解决该问题

package volatiledemo;

public class VolatileDemo05 {

     private static int count = 0;

     public static void main(String[] args) throws InterruptedException {

           for (int i = 0; i < 100; i++) {

                new Thread(new Runnable() {

                     @Override

                     public void run() {

                          synchronized (VolatileDemo05.class) {

                                for (int i = 0; i < 10000; i++) {

                                     count++;

                     System.out.println(Thread.currentThread().getName() + "  count=======>" + count);

                                }

                          }

                     }

                }, "第" + i + "个线程").start();

           }

     }

}

  1. 实例6:利用原子类解决该问题

package volatiledemo;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileDemo06 {

     private static AtomicInteger atomicInteger = new AtomicInteger();

     public static void main(String[] args) throws InterruptedException {

           for (int i = 0; i < 100; i++) {

                new Thread(new Runnable() {

                     @Override

                     public void run() {

                          for (int i = 0; i < 10000; i++) {

                                atomicInteger.incrementAndGet();

                                System.out.println(Thread.currentThread().getName() + "  count=======>" + atomicInteger.get());

                          }

                     }

                }, "第" + i + "个线程").start();

 

           }

     }

}

 

  1. 实例7:重排序引发的问题,该程序中,变量的顺序引发结果不是期望值。

具体表现:结果值有可能存在i=0,j=0;

package volatiledemo;

public class VolatileDemo07 {

     private static int i = 0, j = 0;

     private static int a = 0, b = 0;

     private static int count=0;

     public static void main(String[] args) throws InterruptedException {

          while (true) {

               i = 0;

               j = 0;

               a = 0;

               b = 0;

               Thread t1 = new Thread(new Runnable() {

                    @Override

                    public void run() {

                          a = 1;

                          i = b;

                    }

               });

               Thread t2 = new Thread(new Runnable() {

                    @Override

                    public void run() {

                          b = 1;

                          j = a;

                    }

               });

               t1.start();

               t2.start();

               t1.join();

               t2.join();

                if (i==0&&j==0) {

                     System.out.println("第"+count+++"次,i="+i+";j="+j);

                     break;

               }

                System.out.println("第"+count+++"次,i="+i+";j="+j);

          }

     }

}

  1. 实例8:使用volatile关键字禁止重排序解决上述问题。

private static volatile int i = 0, j = 0;

private static volatile int a = 0, b = 0;

  1. 实例9:以happens-before原则实现多个线程可见性。这种情况下,由于线程间不可见性出现未预期的结果。可能会出现a=3,b=1这种情况。

package volatiledemo;

public class VolatileDemo09 {

     int a = 1;

     int b = 2;

     private void write() {

           a = 3;

           b = a;

     }

     private void read() {

           System.out.println("a=" + a + ", b=" + b);

     }

     public static void main(String[] args) throws InterruptedException {

           while (true) {

                VolatileDemo09 v = new VolatileDemo09();

                new Thread(new Runnable() {

                      

                     @Override

                     public void run() {

                          v.write();

                     }

                }).start();

                new Thread(new Runnable() {

                     @Override

                     public void run() {

                          v.read();

                     }

                }).start();

           }

     }

}

 

  1. 实例10: 以happens-before原则实现多个线程可见性。解决了上述问题。

写volatile变量时,无论前一个操作是什么,都不能重排序

读volatile变量时,无论后一个操作是什么,都不能重排序

当先写volatile变量,后读volatile变量,不能重排序

int a = 1;

volatile int  b = 2;

  1. 实例11: long和double的原子性问题:32位系统读取一个64位的变量,需要分两步执行,每次读取32位,这样就对double和long变量的赋值就会出现问题: 如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。该代码在64系统不会存在问题。

package volatiledemo;

public class VolatileDemo11 {

     public static void main(String[] args) throws InterruptedException {

           String sysNum = System.getProperty("sun.arch.data.model");

          System.out.println("系统的位数:" + sysNum);

           Thread t1 = new Thread(new MyThread(1));

           Thread t2 = new Thread(new MyThread(-1));

           t1.start();

           t2.start();

           t1.join();

           t2.join();

     }

}

 

class MyThread implements Runnable {

     private   static  long a = 0;

     private  long value;

     public void setValue(long value) {

           this.value = value;

     }

     public long getValue() {

           return value;

     }

     public MyThread(long value) {

            this.setValue(value);

     }

 

     @Override

     public void run() {

           int i = 0;

           while (i < 100000) {

                long temp = getValue();

                i++;

                if (temp != 1L && temp != -1L) {

                     System.out.println("出现错误结果" + temp);

                     System.exit(0);

                }

           }

           System.out.println("运行正确");

     }

}

  1. 实例12: 使用volatile关键字解决long和double的原子性问题

private volatile long value;

  1. 实例13: volatile在单例中的双重检查的使用。以下是八种单例模式的写法。
  1. 饿汉式-静态变量: 线程安全

package volatiledemo;

public class Singleton1 {

      private static final Singleton1 INSTANCE=new Singleton1();

      private Singleton1() {}

      public static Singleton1 getInstance() {

            return INSTANCE;

      }

}

  1. 饿汉式-静态代码块:线程安全

package volatiledemo;

 

public class Singleton2 {

      private static final Singleton2 INSTANCE;

      static {

           INSTANCE =new Singleton2();

      }

      private Singleton2() {}

      public static Singleton2 getInstance() {

            return INSTANCE;

      }

}

  1. 懒汉式:线程不安全

package volatiledemo;

public class Singleton3 {

      private static Singleton3 instance;

      private Singleton3() {}

      public static Singleton3 getInstance() {

            if (instance==null) {

                instance=new Singleton3();

           }

            return instance;

      }

}

  1. 懒汉式,加锁,线程安全

package volatiledemo;

public class Singleton4 {

      private static Singleton4 instance;

      private Singleton4() {}

      public synchronized static Singleton4 getInstance() {

            if (instance==null) {

                instance=new Singleton4();

           }

            return instance;

      }

}

  1. 懒汉式,优化锁,线程安全

package volatiledemo;

public class Singleton5 {

      private static Singleton5 instance;

      private Singleton5() {}

      public static Singleton5 getInstance() {

            if (instance==null) {

                 synchronized (Singleton5.class) {

                      instance=new Singleton5();

                }

           }

            return instance;

      }

}

  1. 懒汉式:双重检查机制并且用volatile修饰,线程安全,推荐

package volatiledemo;

public class Singleton6 {

      private volatile static Singleton6 instance;

      private Singleton6() {}

      public static Singleton6 getInstance() {

            if (instance==null) {

                 synchronized (Singleton6.class) {

                     if (instance==null) {

                           instance=new Singleton6();

                     } 

                }

           }

            return instance;

      }

}

  1. 静态内部类单例模式,线程安全

package volatiledemo;

public class Singleton7 {

     private Singleton7() {

     }

     private static class SingletonInstance {

           private static final Singleton7 INSTANCE = new Singleton7();

     }

     public static Singleton7 getInstance() {

           return SingletonInstance.INSTANCE;

     }

}

  1. 枚举实现单例

package volatiledemo;

public enum Singleton8 {

     INSTANCE;

}

  1. 实例14:适用场景: volatile适合纯赋值操作,不适合a++操作。

package volatiledemo;

import java.util.concurrent.atomic.AtomicInteger;

public class VolatileDemo12 implements Runnable{

     private volatile boolean flag=false;

     private AtomicInteger atomicInteger=new AtomicInteger();

                @Override

     public void run() {

           for (int i = 0; i < 10000; i++) {

                switchFlag();

                atomicInteger.incrementAndGet();

           }

     }

     private void switchFlag() {

           //flag=true;

           flag=!flag;  //这句代码是会出问题的

     }

     public static void main(String[] args) throws InterruptedException {

           VolatileDemo12 target=new VolatileDemo12();

           Thread t1=new Thread(target);

           Thread t2=new Thread(target);

           t1.start();

           t2.start();

           t1.join();

           t2.join();

           System.out.println(target.flag);

           System.out.println(target.atomicInteger);

     }

}

  1. 实例14:适用场景: 触发器

package volatiledemo;

 

public class VolatileDemo13 {

     int a = 1;

     int b = 2;

     int c = 3;

     volatile boolean flag = false;

 

     private void write() {

           a = 3;

           b = 4;

           c = 5;

           flag = true;

     }

 

     private void read() {

           // flag 被Volatile修饰,充当了触发器,一旦值为true,此处立即对变量之前的操作可见

           while (flag) {

                System.out.println("a=" + a + ",b=" + b + ",c=" + c);

           }

 

     }

 

     public static void main(String[] args) {

           VolatileDemo13 target=new VolatileDemo13();

           new Thread(new Runnable() {

                @Override

                public void run() {

                     target.write();

                }

           }).start();

           new Thread(new Runnable() {

                @Override

                public void run() {

                      target.read();

                }

           }).start();

     }

}

  • 参考链接:
  1. 参与课堂: https://study.163.com/course/courseMain.htm?courseId=1210074496
  2. long和double的原子性问题: https://www.jianshu.com/p/d898f1b4b15e

本内容由“丫丫”原创。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值