单例的多种写法的区别和线程安全的验证!

目录

 第一种

第二种 DCL

 第三种:静态内部类,懒汉

第四种:enum

即指令重排如何进行验证1:

那么如何将这个案例用在对应的单例模式的指令重排的验证上面来呢?


再次重复的写这个东西的灵感来自:

 

一个博客的介绍和另一个博客的说明的冲突,所以给自己绕懵了,需要来再次梳理一下:

 第一种

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指令重排是可以解决掉的

        参考资料1

        https://mp.weixin.qq.com/s/dU_Mzz76h-qQZvrgeSe44g

指令重排序的参考视频:

https://www.bilibili.com/video/BV1XZ4y157Pj?p=6

参考书籍:java muli-thread programming--高洪岩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

specialApe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值