java_JUC、volatile
JUC:
指java中java.util.concurrent包;(concurrent:并发)
JUC分类:
java.util.concurrent
java.util.concurrent.atomic(atomic:原子性)
java.util.concurrent.locks(locks:锁)
1、volatile是什么?
volatile是java虚拟机提供的轻量级同步机制;
三大特性:
保证可见性;
不保证原子性;
禁止指令重排;
JVM(java虚拟机)
JMM(java内存模型):
Java Memory Model本身是有一种抽象的概念并不真实存在的;
它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段、静态字段、构成数组对象的元素等)的访问方式;
三大特性:
可见性;
原子性;
有序性;
什么是JMM内存模型的可见性?
JMM关于同步的规定:
1、线程解锁前必须把共享变量的值刷新回主内存;
2、线程加锁前必须读取主内存的最新值到自己的工作内存;
3、加锁与解锁是同一把锁;
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间);工作内存是每个线程的私有数据区域,
而java内存模型中规定所有变量都存储在主内存,主内存是共享的内存区域,所有线程都可以访问;但线程对变量的操作必须在工作内存中进行,
首先要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存;不能直接操作主内存的变量,各个线程中
的工作内存存储着主内存的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信必须通过主内存来完成;
JMM内存模型之可见性:线程A与B同时拷贝主内存中的变量,当A操作完成,将操作后的值写进主内存时,将第一时间通知B,B将第一时间知道主内存中的变量值发改变;
volatile可见性代码验证如下:
class MyData{ int number = 0; public void addTo60(){ this.number = 60; } } /** * 1、验证Volatile的可见性 * 1.1、假如int number = 0;,number变量之前根本没有添加volatile关键字修饰,不可见性 */ public class VolatileDemo { public static void main(String[] args) { MyData myData = new MyData();//资源类 new Thread(() -> {//第一个线程A System.out.println(Thread.currentThread().getName()+"\t come in"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName()+"\t updated number value: "+myData.number); },"A").start(); //第二个线程main while (myData.number == 0){ //main线程就一直在这里等待循环,知道number值不再为0 } System.out.println(Thread.currentThread().getName()+"\t mission is over,main get number value:"+myData.number); } }
添加volatile关键字后
volatile int number = 0;
原子性:不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割,需要整体完整,要么同时成功,要么同时失败;
volatile不保证原子性代码验证如下:
class MyData{ volatile int number = 0; public void addTo60(){ this.number = 60; } public void addPlusPlus(){ number++; } } /** * 1、验证volatile的可见性 * 1.1、假如int number = 0;,number变量之前根本没有添加volatile关键字修饰,不可见性 * 1.2、添加了volatile关键字,可以解决可见性问题; * * 2、验证volatile的不保证原子性 * 2.1、原子性指的是什么意思? * 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割, * 需要整体完整,要么同时成功,要么同时失败; */ public class VolatileDemo { public static void main(String[] args) { MyData myData = new MyData();//资源类 for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 1; j <= 1000; j++) { myData.addPlusPlus(); } },String.valueOf(i)).start(); } //需要等待上面20个线程都计算完成,再用main线程取得最终的结果 while (Thread.activeCount() > 2){//后台默认存在的两个线程,main、GC Thread.yield();//等待 } System.out.println(Thread.currentThread().getName()+"\t finally number value:"+myData.number); } }
volatile解决原子性问题?
使用synchronized关键字(大材小用,不推荐);
使用JUC下的AtomicInteger类;
class MyData{ volatile int number = 0; public void addTo60(){ this.number = 60; } public void addPlusPlus(){ number++; } AtomicInteger atomicInteger = new AtomicInteger();//保证原子性的整型封装类 public void addMyAtomic(){ atomicInteger.getAndIncrement();//获取值并且+1 } } /** * 1、验证volatile的可见性 * 1.1、假如int number = 0;,number变量之前根本没有添加volatile关键字修饰,不可见性 * 1.2、添加了volatile关键字,可以解决可见性问题; * * 2、验证volatile的不保证原子性 * 2.1、原子性指的是什么意思? * 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者分割, * 需要整体完整,要么同时成功,要么同时失败; * 2.2、volatile不保证原子性 * 2.3、why * 2.4、如何解决原子性问题? * *加synchronized关键字(大材小用,不推荐) * *使用JUC下的AtomicInteger类 */ public class VolatileDemo { public static void main(String[] args) { MyData myData = new MyData();//资源类 for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 1; j <= 1000; j++) { myData.addPlusPlus(); myData.addMyAtomic(); } },String.valueOf(i)).start(); } //需要等待上面20个线程都计算完成,再用main线程取得最终的结果 while (Thread.activeCount() > 2){//后台默认存在的两个线程,main、GC Thread.yield();//等待 } System.out.println(Thread.currentThread().getName()+"\t finally number value:"+myData.number); System.out.println(Thread.currentThread().getName()+"\t finally atomicInteger value:"+myData.atomicInteger); } }
JMM有序性(禁止指令重排):
volatile禁止指令重排:
线程安全性问题获得保障:
单例模式在多线程中的问题:
public class SingletonDemo2 { public static SingletonDemo2 instance = null; private SingletonDemo2(){ System.out.println("SingletonDemo2"); } public static SingletonDemo2 getInstance(){ if (instance == null){ instance = new SingletonDemo2(); } return instance; } public static void main(String[] args) { for (int i = 0; i < 1000; i++) { new Thread(() ->{ getInstance(); },String.valueOf(i)).start(); } } }
直接使用synchronized关键字:
public static synchronized SingletonDemo2 getInstance(){//大材小用,不合适 if (instance == null){ instance = new SingletonDemo2(); } return instance; }
使用DCL(Double Check Lock 双端检索机制):
//DCL(双端检索机制) public static SingletonDemo2 getInstance(){//由于指令重排机制,不一定100%线程安全 if (instance == null){ synchronized (SingletonDemo2.class){ if (instance == null){ instance = new SingletonDemo2(); } } } return instance; }
单实例volatile分析:
使用volatile关键字解决DCL安全性问题:
public static volatile SingletonDemo2 instance = null;
public class SingletonDemo2 { public static volatile SingletonDemo2 instance = null; private SingletonDemo2(){ System.out.println("SingletonDemo2"); } //DCL(双端检索机制) public static SingletonDemo2 getInstance(){//由于指令重排机制,不一定100%线程安全 if (instance == null){ synchronized (SingletonDemo2.class){ if (instance == null){ instance = new SingletonDemo2(); } } } return instance; } public static void main(String[] args) { for (int i = 0; i < 1000; i++) { new Thread(() ->{ getInstance(); },String.valueOf(i)).start(); } } }