【Java多线程】单例模式与多线程

单例模式大家都不陌生,即让一个类只有一个实例。
单例模式分为懒汉式和饿汉式。
懒汉式☞方法调用时再实例化对象,什么时候用什么时候实例化,比较懒。
饿汉式☞方法调用前对象就已经创建好了,比较有捉急。
本文着重描述懒汉式与多线程的内容。

1.饿汉式

public class SingletonHungary {

    private static SingletonHungary instance = new SingletonHungary();

    private SingletonHungary(){}

    public static SingletonHungary getInstance() {
        return instance;
    }
}
public class Main extends Thread{
    @Override
    public void run() {
        System.out.println(SingletonHungary.getInstance().hashCode());
    }

    public static void main(String[] args) {

        Main[] mts = new Main[10];
        for(int i = 0 ; i < mts.length ; i++){
            mts[i] = new Main();
        }

        for (int j = 0; j < mts.length; j++) {
            mts[j].start();
        }
    }
}

由打印出的hashcode相同说明:他们是同一个对象
这里写图片描述

饿汉式在方法调用之前就创建了静态对象,静态对象是全局唯一的,即使有多个线程访问也是在使用同一个对象。这样的方式是线程不安全的。

2.懒汉式
(懒汉式的实现方式有多种,最终演化出了一种最优的方式,此处只介绍这种最优方式)

public class DubbleSingleton {

    private static DubbleSingleton ds;
    public static DubbleSingleton getDs(){
        if (ds==null){
            System.out.println(Thread.currentThread().getName()+":"+"第一轮判断,我的ds==null,我要去抢创建对象的锁啦!");
            try {
                //模拟初始化对象的准备时间...
                System.out.println(Thread.currentThread().getName()+":"+"模拟初始化对象的准备时间...");
                Thread.sleep(3000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            synchronized (DubbleSingleton.class){
                System.out.println(Thread.currentThread().getName()+":"+"第二轮判断,我的ds==null,我来创建对象啦!");
                if(ds==null){
                    System.out.println(Thread.currentThread().getName()+":"+"哈哈哈哈哈!是我创建的对象!");
                    ds = new DubbleSingleton();
                }else {
                    System.out.println(Thread.currentThread().getName()+":"+"第二轮判断之后,我的ds!=null,看来有人在我之前把对象创建了!");
                }

            }
        }
        return ds;
    }

    public static void main(String[] args){
          Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t2");
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":"+DubbleSingleton.getDs().hashCode());
            }
        },"t3");
        t1.start();
        t2.start();
        t3.start();
    }

}

请读者先思考一个问题再继续往下看:
为什么要判断两次ds==null?

————————————————————-思考分割线—————————————————————-

我们来看一下打印结果:
这里写图片描述
因为懒汉式是方法调用时再创建对象,所以在多线程高并发时为了保证安全,并且高效,在代码块上加了synchronized关键字,多个线程可以同时调用getDS方法,这时他们的ds都是null,但当一个线程在创建一个对象后,由于其他线程已经在第一轮判断时就进入了方法,所以其他线程会继续走synchronized代码块,这时进行第二轮判断,其他线程会在第二轮判断时发现对象已经创建,则不会再继续创建对象。
这里写图片描述

就酱,OVER!

猜你喜欢

转载自blog.csdn.net/zjy15203167987/article/details/80484452