目录
那么如何将这个案例用在对应的单例模式的指令重排的验证上面来呢?
再次重复的写这个东西的灵感来自:
一个博客的介绍和另一个博客的说明的冲突,所以给自己绕懵了,需要来再次梳理一下:
第一种
class Singleton
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
}
第二种 DCL
public class Java3y {
private Java3y() {
}
private static volatile Java3y java3y = null;
public static Java3y getJava3y() {
if (java3y == null) {
// 将锁的范围缩小,提高性能
synchronized (Java3y.class) {
// 再判断一次是否为null
if (java3y == null) {
java3y = new Java3y();
}
}
}
return java3y;
}
}
volatile的原理如下:
第三种:静态内部类,懒汉
public class Java3y {
private Java3y() {
}
// 使用内部类的方式来实现懒加载
private static class LazyHolder {
// 创建单例对象
private static final Java3y INSTANCE = new Java3y();
}
// 获取对象
public static final Java3y getInstance() {
return LazyHolder.INSTANCE;
}
}
第四种:enum
package com.special.thread.javamulthreadbook;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author liuYC
* @ClassName EnumSingleDemo
* @Description TODO
* @date 2021/7/23 10:13
* <p>
* 通过enum实现单例模式的demo
* <p>
* 同时考虑到设计模式的优雅性
* 定义对应的enum 同时提供一个方法供返回
*/
public enum EnumSingleDemo {
connectionFactory;
private Connection connection;
private EnumSingleDemo() {
System.out.println("constrctor is used");
try {
connection = DriverManager.getConnection("", "", "");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
}
/**
* 定义线程,输出enum定义的 HASHcode,验证线程是否是同一个
*/
class EnumSingleThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5; i++) {
System.out.println("i thread hashcode is ……");
// 注意调用 enum 的方法是: 类名.factory.method ----其中factory就是第一个的枚举声明
System.out.println(EnumSingleDemo.connectionFactory.getConnection().hashCode());
;
}
}
}
/**
* 程序的main 入口
*/
class MainDemo {
public static void main(String[] args) {
final EnumSingleThread enumSingleThread1 = new EnumSingleThread();
final EnumSingleThread enumSingleThread2 = new EnumSingleThread();
final EnumSingleThread enumSingleThread3 = new EnumSingleThread();
enumSingleThread1.start();
enumSingleThread3.start();
enumSingleThread2.start();
}
}
结果到最后才发现,其实线程安全最起码都有三种,如果synchronized 和对应的 DCL不进行合并,都是四种哦!
最后加上自己的额思考部分:
即指令重排如何进行验证1:
代码部分:
package com.special.threademo.concurrency.synchcriozed;
import org.springframework.scheduling.concurrent.ScheduledExecutorTask;
import java.util.HashSet;
import java.util.Set;
/**
* @author liuYC
* @ClassName VolatileDemo
* @Description TODO
* @date 2021/7/20 10:46
*
* 测试的指令重排序的原理就是jvm,考虑执行的效率,会在赋值等操作上,操作同类的先执行的优先考虑,
* 所以结果就就会有
* [ a=0 b=0, // b = x; a = y;先执行
* a=1 b=1] // y = 1; x = 1;先执行
* [ a=0 b=1, a=1 b=1] 等// a = y x = 1;先执行
* 其本质就是:
* 不完全按照代码的顺序进行对应的执行
* 那么如何用在考入对应的单例模式的指令重排的验证上面来呢?
*/
public class VolatileDemo {
static int a = 0, b = 0, x = 0, y = 0;
public static void main(String[] args) throws InterruptedException {
Set<String> result = new HashSet<>();
for (int i = 0; i < 10000000; i++) {
Thread t1 = new Thread(() -> {
a = y;
x = 1;
});
Thread other = new Thread(() -> {
b = x;
y = 1;
});
t1.start();
other.start();
t1.join();
other.join();
result.add(" a=" + a + " b=" + b);
System.out.println(result);
}
}
}
运行结果:
那么如何将这个案例用在对应的单例模式的指令重排的验证上面来呢?
思路如下:
代码如下:
public class Java3y { private Java3y() { } private static Java3y java3y = null; public static Java3y getJava3y() { if (java3y == null) { // 将锁的范围缩小,提高性能 synchronized (Java3y.class) { // 再判断一次是否为null if (java3y == null) { java3y = new Java3y(); } } } // 查看实例的各部分的值并且多个线程,之间现实不同,那么就可以证明volatile的修饰是必要的 /* */ return java3y; } }
结果如下:
两篇博客来自于:
通过 Javap -v -c -p 查看对应的汇编码可以证明发生了指令重排,并且加上volatile指令重排是可以解决掉的
https://mp.weixin.qq.com/s/dU_Mzz76h-qQZvrgeSe44g
指令重排序的参考视频:
https://www.bilibili.com/video/BV1XZ4y157Pj?p=6
参考书籍:java muli-thread programming--高洪岩