1.谈谈你对volatile的理解
volatile是java虚拟机提供的轻量级的同步机制
有三大特性:
保证可见性,线程休修改数据后其他线程可以及时感知
不保证原子性
禁止指令重排
JMM模型 :java线程操作内存中数据的过程需要各个线程从主内存将数据拷贝到自己的工作内存,操作完毕后再写入主内存,主内存中的数据各个线程之间是共享的,但是线程间是不能互相访问对方的工作内存中的数据的。
需要保证可见性 :线程间及时通信
不原子性:原子性操作
, 禁止指令重排:指令代码有序性 才可以保证线程安全
代码可以验证 MyData中num没有添加volatile关键字
线程操作原子 原子类Mydata
package com.volalite;
/**
* @author liuxu
* @date 2021/11/9 22:16
*/
class MyData{
//int num = 0;
volatile int num = 0;
public void addTo26(){
this.num=26;
}
public void add1(){
num++;
}
}
可见性验证
package com.volalite;
import java.util.concurrent.TimeUnit;
/**
* @author liuxu
* @date 2021/11/9 21:40
*/
public class VolatileDome {
/**
* 验证可见性
*/
public static void main(String[] args) {
MyData myData = new MyData();
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"\t进来了");
try {
/**
* 暂停1s保证其他线程可以在操作数前读取Mydata的数据
*/
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
myData.addTo26();
System.out.println(Thread.currentThread().getName()+"感知的num值是:"+myData.num);
},"线程1").start();
System.out.println(Thread.currentThread().getName()+"感知的num值是:"+myData.num);
//如果mian线程无法感知num的变化,将一直阻塞,
while (myData.num==0){
}
System.out.println(Thread.currentThread().getName()+"感知到线程间数据操作");
}
}
mian线程一直认为num==0,一直阻塞

MyData中num添加volatile关键字后

可以看到volatile使得线程间数据操作可以互相感知
不保证原子性验证
package com.volalite;
/**
* @author liuxu
* @date 2021/11/9 22:17
*/
public class VolatileDemo2 {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 200; j++) {
myData.add1();
}
}).start();
}
while (Thread.activeCount()>2){
Thread.yield();//运行线程大于2 说明10个循环线程还没运行完毕,在此等待将10个线程运行完毕,主线程不载阻塞
}
System.out.println(myData.num);
}
}
结果

原子性问题解决
用AtomicInteger代替int
package com.volalite;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author liuxu
* @date 2021/11/9 22:16
*/
class MyData{
//int num = 0;
AtomicInteger num = new AtomicInteger(0);
public void add1(){
num.incrementAndGet();
}
}

jvm指令重排案例1
package com.volalite;
import java.util.concurrent.TimeUnit;
/**
* @author liuxu
* @date 2021/11/9 22:54
*/
public class VisibilityTest extends Thread {
private boolean stop;
@Override
public void run() {
int i = 0;
while (!stop) {
i++;
}
System.out.println("finish loop,i=" + i);
}
public void stopIt() {
stop = true;
}
public boolean getStop() {
return stop;
}
public static void main(String[] args) throws Exception {
VisibilityTest v = new VisibilityTest();
v.start();
Thread.sleep(1000);
v.stopIt();
Thread.sleep(2000);
System.out.println("finish main");
System.out.println(v.getStop());
}
}
jvm指令重排案例2
package com.volalite;
import java.util.concurrent.TimeUnit;
/**
* @author liuxu
* @date 2021/11/9 22:54
*/
public class ReSortDemo {
volatile int a =0;
boolean flag =false;
public void methods1(){
a=1;
flag =true;
}
public void methods2(){
if(flag){
a=a+5;
System.out.println("methods2结果是:"+a);
}
}
public static void main(String[] args) {
ReSortDemo reSortDemo = new ReSortDemo();
new Thread(()->{
reSortDemo.methods1();
reSortDemo.methods2();
}).start();
new Thread(()->{
reSortDemo.methods2();
reSortDemo.methods1();
}).start();
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(reSortDemo.a);
}
}
如果指令重排存在,案例1应该存在程序阻塞的情况,案例2应该输出不应该是
methods2结果是:6
methods2结果是:11
不知道是编译器的原因还是其他原因,重拍现象没有出现。
单例模式的代码
package com.volalite;
/**
* @author liuxu
* @date 2021/11/10 20:55
*/
public class SingleTonDemo {
private static SingleTonDemo instance = null;
public SingleTonDemo() {
System.out.println("SingleTonDemo构造方法");
}
public static SingleTonDemo getInstance(){
if(instance==null){
instance =new SingleTonDemo();
}
return instance;
}
public static void main(String[] args) {
System.out.println(SingleTonDemo.getInstance()==SingleTonDemo.getInstance());
System.out.println(SingleTonDemo.getInstance()==SingleTonDemo.getInstance());
System.out.println(SingleTonDemo.getInstance()==SingleTonDemo.getInstance());
}
}
输出三个true, 且“SingleTonDemo构造方法”只输出一次,明只有一个对象被构造
多线程情况下,构造方法执行了不止一次
package com.volalite;
/**
* @author liuxu
* @date 2021/11/10 20:55
*/
public class SingleTonDemo {
private static SingleTonDemo instance = null;
public SingleTonDemo() {
System.out.println("SingleTonDemo构造方法");
}
public static SingleTonDemo getInstance(){
if(instance==null){
instance =new SingleTonDemo();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
SingleTonDemo.getInstance();
},"Thread"+i).start();
}
}
}

首先给 getInstance()添加synchronized可以解决问题
双端检索机制不一定安全,因为有指令重排的存在
package com.volalite;
/**
* @author liuxu
* @date 2021/11/10 20:55
*/
public class SingleTonDemo {
private static SingleTonDemo instance = null;
public SingleTonDemo() {
System.out.println("SingleTonDemo构造方法");
}
public static synchronized SingleTonDemo getInstance(){
if(instance==null){
synchronized (SingleTonDemo.class){
if(instance==null) {
instance = new SingleTonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
SingleTonDemo.getInstance();
},"Thread"+i).start();
}
}
}
DCL(双端检索不安全的原因),jvm初始化对象的过程
memoty = allocate();//1.分配内存空间
instance(memoty );//2.初始化对象
instance = memoty ;//3.设置instance 指向刚分配的内存地址
步骤2,3不存在依赖关系,多线程情况下可能会发生指令重排,3,2
执行,instance 有指向,但是指向了没有初始化的对象,存在线程安全问题。
给 instance 添加volatile关键字可以解决问题
private static volatile SingleTonDemo instance = null;
单例模式线程安全问题解决:1. getInstance 方法添加synchronized(重锁,不推荐)
2.DCL双端检索加volatile,推荐
24万+

被折叠的 条评论
为什么被折叠?



