你的同学在学习,你的对手在磨刀,你的闺蜜在减肥,你还等什么?
JMM-----原子性代码证明(面试题笔记3)
package jMM;
import java.util.concurrent.TimeUnit;
//1.
public class Jmm_kejianxing {
volatile int number =0; //原主机的数据
public void addT060(){
this.number =60; //之后数据改变为60
}
//请注意,此时的number前面是加了volatile,所以不保证原子性
public void addPlusPlus(){
number++;
}
}
/*
* 1.验证volatile的可见性
* 1.1 假如 int number =0 ;number变量之前根本没有添加volatile关键字修饰
* 1.2 添加了volatile,拥有了可见性
*
* 2.验证volatile不保证原子性
* 2.1原子性指的是什么意思?
* 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整
* 要么同时成功,要么同时失败。
*
* 2.2不保证原子性案例演示
* */
class VolatileDemo{
public static void main(String[] args) //main方法是一切方法的运行入口
{
Jmm_kejianxing jmm_kejianxing =new Jmm_kejianxing();//资源类
//俩个实现多线程方法---1.实现Runnable接口,2.继承Thread类
// new Thread(new Runnable() {
// @Override
// public void run() {
//
// }
// }).start();
new Thread(
() -> {
System.out.println(Thread.currentThread().getName()+"\t come in");
//暂停一会儿线程
try {TimeUnit.SECONDS.sleep(3);}catch (InterruptedException e){e.printStackTrace();}
jmm_kejianxing.addT060();
System.out.println(Thread.currentThread().getName()+"\t update number value"+jmm_kejianxing.number);
},"AAA").start();
//第二个线程就是我们的main线程
while (jmm_kejianxing.number == 0){
//main 线程就一直再这里等待循环,直到number值不再等于0
}
System.out.println(Thread.currentThread().getName()+"\t mission is over");
}
}
class VolatileDemo1{
public static void main(String[] args) {
Jmm_kejianxing jmm_kejianxing = new Jmm_kejianxing();
for (int i = 1; i < 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
jmm_kejianxing.addPlusPlus();
}
}, String.valueOf(i)).start();
}
//需要等待上面20个线程都全部计算完成后,再用main线程取得最终结果值是多少?
// //暂停 一会儿线程
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(Thread.activeCount()>2){
Thread.yield();}
System.out.println(Thread.currentThread().getName()+"\t finally number value:"+ jmm_kejianxing.number);
}
//volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
public static void seeokByVolatile()
{
Jmm_kejianxing jmm_kejianxing = new Jmm_kejianxing();//资源类
new Thread(
() -> {
System.out.println(Thread.currentThread().getName() + "\t come in");
//暂停一会儿线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
jmm_kejianxing.addT060();
System.out.println(Thread.currentThread().getName() + "\t update number value" + jmm_kejianxing.number);
}, "AAA").start();
//第二个线程就是我们的main线程
while (jmm_kejianxing.number == 0) {
//main 线程就一直再这里等待循环,直到number值不再等于0
}
System.out.println(Thread.currentThread().getName() + "\t mission is over");
}
}
main finally number value:17966
Process finished with exit code 0
详细解释:
number++在多线程下是非线程安全的,所以结果并不是20000,加synchronized,----可以解决,但是有点大材小用了。
因为volatile是轻量级,所以不保证原子性,所以数值小于20000,出现数据丢失现象。
(虽然每个线程都具备了可见性,但是由于速度快,导致数据被第一个线程写入,输出。当第二个线程接收到改变的数据时,输出时,会被第一个线程覆盖掉。)
idea Java集成命令,需要配置,(左击Extenal TOOLS-》Javap -c)
深度学习Java字节码:
class Jmm_kejianxing {
volatile int number =0;
public void add(){
number++;
}
}
aload 0 ---------从局部变量0中装载引用类型值
dup -------------部一个字长内容
n++步骤:
1.n=0初始值对应getfield
2.iconst——1----n++要加一层
iadd
3.putfield---写回主内存
方法二解决不保证原子性:
public class Jmm_kejianxing {
volatile int number =0; //原主机的数据
public void addT060(){
this.number =60; //之后数据改变为60
}
//请注意,此时的number前面是加了volatile,所以不保证原子性
public void addPlusPlus(){
number++;
}
AtomicInteger atomicInteger =new AtomicInteger();
public void addAtomic(){
atomicInteger.getAndIncrement();
}
}
.验证volatile的可见性
* 1.1 假如 int number =0 ;number变量之前根本没有添加volatile关键字修饰
* 1.2 添加了volatile,拥有了可见性
*
* 2.验证volatile不保证原子性
* 2.1原子性指的是什么意思?
* 不可分割,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整
* 要么同时成功,要么同时失败。
*
* 2.2不保证原子性案例演示
*
* 2.3如何解决与原子性?
* 1,加synchronized
* 2,使用我们的juc下AtomicInteger
class VolatileDemo1{
public static void main(String[] args) {
Jmm_kejianxing jmm_kejianxing = new Jmm_kejianxing();
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for (int j = 1; j <= 1000; j++) {
jmm_kejianxing.addPlusPlus();
jmm_kejianxing.addAtomic();
}
}, String.valueOf(i)).start();
}
//需要等待上面20个线程都全部计算完成后,再用main线程取得最终结果值是多少?
// //暂停 一会儿线程
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(Thread.activeCount()>2){
Thread.yield();}
System.out.println(Thread.currentThread().getName()+"\t int type finally number value:"+ jmm_kejianxing.number);
System.out.println(Thread.currentThread().getName()+"\t AtomicInteger type,finally number value:"+ jmm_kejianxing.atomicInteger);
}
//volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
public static void seeokByVolatile()
{
Jmm_kejianxing jmm_kejianxing = new Jmm_kejianxing();//资源类
new Thread(
() -> {
System.out.println(Thread.currentThread().getName() + "\t come in");
//暂停一会儿线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
jmm_kejianxing.addT060();
System.out.println(Thread.currentThread().getName() + "\t update number value" + jmm_kejianxing.number);
}, "AAA").start();
//第二个线程就是我们的main线程
while (jmm_kejianxing.number == 0) {
//main 线程就一直再这里等待循环,直到number值不再等于0
}
System.out.println(Thread.currentThread().getName() + "\t mission is over");
}
}
结果:
main int type finally number value:19097
main AtomicInteger type,finally number value:20000
Process finished with exit code 0