单例模式的学习

单例模式简单的说就是在整个运行环境下都只存在一个对象。

单例模式实现的方式有如下几种:

立即加载/饿汉模式

立即加载指的是使用类的时候就已经将对象加载完毕
立即加载有”紧急、急迫“的意味,所以也叫做 饿汉模式
代码实现:

 */
public class MyObject {
    private static MyObject myObject=new MyObject();

    public MyObject() {
    }
    public static MyObject getInstance(){
        return myObject;
    }
}

延迟加载/懒汉模式

延迟加载是指调用 get()方法时实例才被创建,常见的实现是在get()方法中进行new实例化
实现代码:

public class MyObject {
    private static MyObject myObject;

    public MyObject() {
    }

    public static MyObject getInstance(){
        //延迟加载
        if(myObject!=null){
        }else{
            myObject=new MyObject();
        }
        return myObject;

    }
}

多线程下的单例模式

上面那样的写法在普通情况下是没有问题的,但是在多线程模式下可能出现非线程安全问题。

饿汉模式的线程安全问题

如果我们在饿汉模式下要给对象设置值:

public class MyObject {
    private static MyObject myObject=new MyObject();

    private  String username;
    private  String password;

    public MyObject() {
    }
    public static MyObject getInstance(String username,String password){
        myObject.username=username;
        myObject.password=password;
        return myObject;
    }

    @Override
    public String toString() {
        return "账号是"+username+"  密码是"+password;
    }
}

线程类:

public class MyThread extends Thread {


    public String username;
    public String password;
    @Override
    public void run() {
        System.out.println(MyObject.getInstance(username,password));
        super.run();
    }
}

运行类:

public class Run {
    public static void main(String[] args) {
        MyThread[] threads = new MyThread[10];
        for (int i = 0; i < 10; i++) {
            threads[i]=new MyThread();
            threads[i].username=""+i;
            threads[i].password=""+i;
        }
        for (int i = 0; i < 10; i++) {
            threads[i].start();
        }
    }
}

我们想的是连续打印十行,每一行从0-9这样打印下来,但是这种多线程情况下,有些值会被覆盖:
在这里插入图片描述

懒汉模式的线程安全问题:

public class MyObject {
    private static MyObject myObject;

    public MyObject() {
    }

    public static MyObject getInstance(){
        //延迟加载
        if(myObject!=null){
        }else{
            myObject=new MyObject();
        }
        return myObject;

    }
}

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(MyObject.getInstance());
        super.run();
    }
}
public class Run {
    public static void main(String[] args) {
//        MyThread t1 = new MyThread();
//        t1.start();
        MyThread[] ts=new MyThread[10];
        for (int i = 0; i < 10; i++) {
            ts[i]=new MyThread();
        }
        for (int i = 0; i < 10; i++) {
            ts[i].start();
        }
    }
}

我们原意是每个线程获取到的对象都是一样的,所以打印的值都一样,但是实际运行如下:
在这里插入图片描述
可以看到,得到的对象并不全部相同

饿汉加载的线程安全解决方案

饿汉模式下的问题是因为每个线程都需要设置值,所以解决方案只能是禁止设置值,值应该在初始化的时候就确定好

懒汉加载的线程安全解决方案

使用synchronized关键字

直接在 getInstance() 方法前面增加一个 synchronized 关键字,这样线程之间就是同步运行的,不会产生同时创建对象的情况

DCL锁(双检查锁)

代码实现:

public class MyObject {
    private static volatile MyObject myObject;

    public MyObject() {
    }

    public static MyObject getInstance(){
        if(myObject != null){
        }else {
            synchronized (MyObject.class){
                if(myObject==null)
                    myObject=new MyObject();
            }
        }
        return myObject;
    }
}

如上,就是使用了synchronized和volatile这两种锁一起实现。

使用synchronized锁是为了防止同时创建对象的情况,使用volatile则是为了可见性,假设A和B线程都进入了else语句块,此时在它们的线程内存内myobject都是null,当A创建完之后,B进入同步块,此时如果没有可见性的话则B线程又会创建一个新的对象,导致线程安全问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值