商务网站建设策划思路,平台网站如何做推广方案设计,cms快速建站,阿里云域名注册入口官网volatile是java虚拟机提供的轻量级的同步机制#xff1a;
1.保证可见性#xff1a;线程之间可见性(及时通知) 2.不保证原子性 3.禁止指令重排
先了解一下jvm同步 由于JVM运行程序的实体是线程#xff0c;而每个线程创建时JVM都会为其创建一个工作内存#xff08;或者称为…volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性线程之间可见性(及时通知) 2.不保证原子性 3.禁止指令重排
先了解一下jvm同步 由于JVM运行程序的实体是线程而每个线程创建时JVM都会为其创建一个工作内存或者称为栈空间工作内存是每个线程的私有数据区域而java内存模型中规定所有变量都存储在主内存主内存是共享内存区域所有线程都可以访问但线程对变量的操作读取赋值等必须在工作内存中进行首先要将变量从主内存拷贝到自己的栈空间然后对变量进行操作操作完成后再将变量写回主内存不能直接操作主内存中的变量各个线程中的工作内存中存储着主内存中的变量副本拷贝因此不同的线程间无法访问对方的工作内存线程间的通信传值必须通过主内存来完成。
一、volatile的可见性demo验证
一、没有加volatile
package Volatile;import java.util.concurrent.TimeUnit;/*** volatile的可见性* demo*/
class MyData{int number 0;public void addTO60(){this.number 60;}
}public class demo {public static void main(String[] args) {MyData myData new MyData();new Thread(() - {System.out.println(Thread.currentThread().getName() come in);//让线程等待3stry {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTO60();System.out.println(Thread.currentThread().getName() updated number value: myData.number);},AAA).start();//第二个线程是main线程while(myData.number 0){//循环等待}System.out.println(Thread.currentThread().getName());}
}
运行结果可以看出来会卡在while循环处 二、加上volatile后
/*** volatile的可见性* demo*/
class MyData{volatile int number 0;public void addTO60(){this.number 60;}
}public class demo {public static void main(String[] args) {MyData myData new MyData();new Thread(() - {System.out.println(Thread.currentThread().getName() come in);//让线程等待3stry {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTO60();System.out.println(Thread.currentThread().getName() updated number value: myData.number);},AAA).start();//第二个线程是main线程while(myData.number 0){//循环等待}System.out.println(Thread.currentThread().getName()mission is over;main get number value:myData.number);}
}
结果
结果可以看出当其他线程修改了主内存空间的值时加上了volatile主内存空间的值改变后会及时通知其他线程主物理内存的值被修改。
二不保证原子性demo验证
下面代码20个线程每个线程进行1000次number理论上结果是两万实际运行 public static void main(String[] args) {MyData myData new MyData();for (int i 1; i 20; i) {new Thread(() - {for (int j 0; j 1000 ; j) {myData.addPlusPlus();}},String.valueOf(i)).start();}while(Thread.activeCount()2){Thread.yield();}System.out.println(myData.number);}结果并不是两万所以说不能保证原子性不能保证结果一致性存在线程安全问题
解决办法
1.synchronized(有点小题大做)
synchronized public void addPlusPlus() {this.number;
}2.使用AtomicInteger
class MyData {volatile int number 0;public void addTO60() {this.number 60;}public void addPlusPlus() {this.number;}AtomicInteger atomicInteger new AtomicInteger();public void atomicPlusPlus(){atomicInteger.getAndIncrement();}
}
public class demo {public static void main(String[] args) {MyData myData new MyData();for (int i 1; i 20; i) {new Thread(() - {for (int j 0; j 1000 ; j) {myData.addPlusPlus();myData.atomicPlusPlus();}},String.valueOf(i)).start();}while(Thread.activeCount()2){Thread.yield();}System.out.println(myData.number);System.out.println(myData.atomicInteger);}就是用AtomicInteger来代替number用getAndIncrement来代替numberAtomic相关内容可以看API了解方法怎么用。就可以保证原子性。
三、禁止指令重排
理解这里需要了解一点编译器编译器在编译时会有个优化指令重排在多线程下指令重排会造成线程安全问题最终一致性无法保证
多线程环境中线程的交替执行由于编译器优化重排的存在两个线程中使用的变量能否保证一致性是无法确定的结果无法预测。
例单例模式为例在多线程下普通的单例模式并不适用
class SingletonDemo{private static SingletonDemo instance null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()\t调用构造方法SingletonDemo);}public static SingletonDemo getInstance(){if (instance null){instance new SingletonDemo();}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i 0; i 10; i) {new Thread(()-{SingletonDemo.getInstance();}).start();}}
}结果可以看到并不是只会创建一个对象
使用dlc(Double Check Lock双端检索机制)代码如下
class SingletonDemo{private static SingletonDemo instance null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()\t调用构造方法SingletonDemo);}// public static SingletonDemo getInstance(){
// if (instance null){
// instance new SingletonDemo();
// }
// return instance;
// }//使用dlc双端检索机制public static SingletonDemo getInstance(){if (instance null){synchronized(SingletonDemo.class){if (instance null){instance new SingletonDemo();}}}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i 0; i 10; i) {new Thread(()-{SingletonDemo.getInstance();}).start();}}
}结果
那么这样就可以了吗?
不行因为编译器会进行指令重排instance new SingletonDemo();编译器会拆分成三步 1.memory allocate(); //分配对象内存空间 2.instance(memory)//初始化对象 3.instance memory; //设置instance指向刚分配的内存地址此时instacenull
所以可能出现另一个线程进入了第一个if (instance null){时instance的引用对象还未初始化完成所以要加入volatile来禁止指令重排
package Volatile;class SingletonDemo{private static volatile SingletonDemo instance null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()\t调用构造方法SingletonDemo);}// public static SingletonDemo getInstance(){
// if (instance null){
// instance new SingletonDemo();
// }
// return instance;
// }//使用dlc双端检索机制public static SingletonDemo getInstance(){if (instance null){synchronized(SingletonDemo.class){if (instance null){instance new SingletonDemo();}}}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i 0; i 10; i) {new Thread(()-{SingletonDemo.getInstance();}).start();}}
}