java单例双重检查锁为什么需要加volatile关键字
答案:2 悬赏:30 手机版
解决时间 2021-03-06 05:27
- 提问者网友:献世佛
- 2021-03-05 19:44
java单例双重检查锁为什么需要加volatile关键字
最佳答案
- 五星知识达人网友:摆渡翁
- 2021-03-05 20:25
没有volatile修饰的uniqueinstance
[java] view plain copy
public class singleton {
private static singleton uniqueinstance;
private singleton(){
}
public static singleton getinstance(){
if(uniqueinstance == null){ //#1
synchronized(singleton.class){ //#2
if(uniqueinstance == null){ //#3
uniqueinstance = new singleton(); //#4
system.out.println(thread.currentthread().getname() + ": uniqueinstance is initalized..."); //#5.1
} else {
system.out.println(thread.currentthread().getname() + ": uniqueinstance is not null now..."); //#5.2
}
}
}
return uniqueinstance;
}
}
这样可能会导致结果 singleton被实例化两次 ,这样就不符合单例的特点
原因分析:
1. thread2进入#1, 这时子线程的uniqueinstance都是为空的,thread2让出cpu资源给thread3
2. thread3进入#1, 这时子线程的uniqueinstance都是为空的, thread3让出cpo资源给thread2
3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueinstance。thread2执行完毕让出cpo资源给thread3
4. thread3接着#1跑下去,跑到#3的时候,由于#1里面拿到的uniqueinstance还是空(并没有及时从thread2里面拿到最新的),所以thread3仍然会执行#4,#5.1
5. 最后在thread2和thread3都实例化了uniqueinstance
例子2:用volatile修饰的uniqueinstance
这里就不贴重复的代码了,因为只是加多一个volatile来修饰成员变量:uniqueinstance,
这样可以创建出一个单例实例。
原因分析:
volatile(java5):可以保证多线程下的可见性;
读volatile:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。
写volatile: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。
1. thread2进入#1, 这时子线程的uniqueinstance都是为空的(java内存模型会从主线程拷贝一份uniqueinstance=null到子线程thread2),thread2让出cpu资源给thread3
2. thread3进入#1, 这时子线程的uniqueinstance都是为空的(java内存模型会从主线程拷贝一份uniqueinstance=null到子线程thread2), thread3让出cpo资源给thread2
3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueinstance(由于是volatile修饰的变量,会马上同步到主线程的变量去)。thread2执行完毕让出cpo资源给thread3
4. thread3接着#1跑下去,跑到#3的时候,会又一次从主线程拷贝一份uniqueinstance!=null回来,所以thread3就直接跑到了#5.2
5. 最后在thread3不再会重复实例化uniqueinstance了
[java] view plain copy
public class singleton {
private static singleton uniqueinstance;
private singleton(){
}
public static singleton getinstance(){
if(uniqueinstance == null){ //#1
synchronized(singleton.class){ //#2
if(uniqueinstance == null){ //#3
uniqueinstance = new singleton(); //#4
system.out.println(thread.currentthread().getname() + ": uniqueinstance is initalized..."); //#5.1
} else {
system.out.println(thread.currentthread().getname() + ": uniqueinstance is not null now..."); //#5.2
}
}
}
return uniqueinstance;
}
}
这样可能会导致结果 singleton被实例化两次 ,这样就不符合单例的特点
原因分析:
1. thread2进入#1, 这时子线程的uniqueinstance都是为空的,thread2让出cpu资源给thread3
2. thread3进入#1, 这时子线程的uniqueinstance都是为空的, thread3让出cpo资源给thread2
3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueinstance。thread2执行完毕让出cpo资源给thread3
4. thread3接着#1跑下去,跑到#3的时候,由于#1里面拿到的uniqueinstance还是空(并没有及时从thread2里面拿到最新的),所以thread3仍然会执行#4,#5.1
5. 最后在thread2和thread3都实例化了uniqueinstance
例子2:用volatile修饰的uniqueinstance
这里就不贴重复的代码了,因为只是加多一个volatile来修饰成员变量:uniqueinstance,
这样可以创建出一个单例实例。
原因分析:
volatile(java5):可以保证多线程下的可见性;
读volatile:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。
写volatile: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。
1. thread2进入#1, 这时子线程的uniqueinstance都是为空的(java内存模型会从主线程拷贝一份uniqueinstance=null到子线程thread2),thread2让出cpu资源给thread3
2. thread3进入#1, 这时子线程的uniqueinstance都是为空的(java内存模型会从主线程拷贝一份uniqueinstance=null到子线程thread2), thread3让出cpo资源给thread2
3. thread2会依次执行#2,#3,#4, #5.1,最终在thread2里面实例化了uniqueinstance(由于是volatile修饰的变量,会马上同步到主线程的变量去)。thread2执行完毕让出cpo资源给thread3
4. thread3接着#1跑下去,跑到#3的时候,会又一次从主线程拷贝一份uniqueinstance!=null回来,所以thread3就直接跑到了#5.2
5. 最后在thread3不再会重复实例化uniqueinstance了
全部回答
- 1楼网友:刀戟声无边
- 2021-03-05 22:02
1. volatile关键字起到提醒JVM这个变量永远去内存当中去获取值(有时候值会被写到寄存器当中)
2. 被volatile声明的变量,那么所有的线程都会得到一样的值。
我要举报
如以上问答信息为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
推荐资讯