Java的单例模式

引言

平时经常使用别人写好的框架,但是没有过多的深究,之前写过相关的博文,只是缺少代码解释,所以在这里进行补充,并且做个复习。

什么是单例

所谓的单例,也就是单一实例,再进一步说,也就是再内存中只有一个对象。想到一个罗曼蒂克的词语,理科生应该能够体会得到内涵,有且仅有。🤭。

类别

懒汉式:也就是说真正用到的时候才会创建实例。
饿汉式:在一开始的时候就创建这个实例。
双重检查锁:个人认为是懒汉式的优化版。

代码实现

懒汉式

Lazy.java

package top.zunmx.demo;

public class Lazy {
    public static Lazy obj; // 静态变量
    private Lazy(){} // 私有构造方法
    public synchronized static Lazy getInstance(){
        if(obj==null){
            obj = new Lazy();
            System.out.println("LAZE-CREATED BY "+Thread.currentThread().getName());
        }
        return obj;
    }
    public String func(){
        return "LAZY";
    }
}

LTest.java

package top.zunmx.demo;

public class LTest extends Thread {
    public static void main(String[] args) {
        LTest t1 = new LTest();
        t1.start();
        LTest t2 = new LTest();
        t2.start();
        LTest t3 = new LTest();
        t3.start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "<-->" + Lazy.getInstance().func() + "<-->" + Lazy.getInstance().hashCode());

    }
}

运行结果

LAZE-CREATED BY Thread-1
Thread-1<-->LAZY<-->1700405589
Thread-0<-->LAZY<-->1700405589
Thread-2<-->LAZY<-->1700405589

饿汉式

Hunger.java

package top.zunmx.demo;

public class Hunger {
    private static Hunger hunger = new Hunger();
    private Hunger (){}
    public static Hunger getInstance(){
        return hunger;
    }
    public String func(){
        return "HUNGER";
    }
}

HTest.java

package top.zunmx.demo;

public class HTest extends Thread {
    public static void main(String[] args) {
        HTest t1 = new HTest();
        t1.start();
        HTest t2 = new HTest();
        t2.start();
        HTest t3 = new HTest();
        t3.start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "<-->" + Hunger.getInstance().func() + "<-->" + Hunger.getInstance().hashCode());
    }
}

运行结果

Thread-1<-->HUNGER<-->1030697658
Thread-2<-->HUNGER<-->1030697658
Thread-0<-->HUNGER<-->1030697658

双重检查锁

LockTest

package top.zunmx.demo;

public class LockTest extends Thread {
    private LockTest() {
    }

    private volatile static LockTest lockTest = null;

    public static LockTest getInstance() {
        if (lockTest == null) {
            synchronized (LockTest.class) {
                if (lockTest == null) {
                    lockTest = new LockTest();
                    System.out.println("CREATE OBJECT BY " + Thread.currentThread().getName());
                }
            }
        }
        return lockTest;
    }
    public String func(){
        return "LOCK";
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "<-->" + LockTest.getInstance().func() + "<-->" + LockTest.getInstance().hashCode());
    }
    public static void main(String[] args) {
        LockTest t1 = new LockTest();
        LockTest t2 = new LockTest();
        LockTest t3 = new LockTest();
        t1.start();
        t2.start();
        t3.start();
    }
}

结论

懒汉式: 懒汉式只有在调用的时候才会创建, 当创建实例时,为了保证线程的安全,需要在获取实例的时候加锁,然而每次获取实例对象都需要加锁和解锁,对象创建一次就好,一般情况取对象时上锁显得多此一举,上锁和解锁是需要耗费资源和时间的,所以这种形式不是很优雅.

饿汉式: 当加载类的时候就创建对象,一劳永逸,虽然看起来没有什么问题,但是如果这个类加载多次,会导致创建多次实例,这样也会造成性能的浪费.

双重检查锁: 这里涉及到了volatile关键字, 如果了解汇编语言,就会知道,在我们的代码中的一行, 对应的机器语言可能是多条指令, 这些指令由编译器进行优化的时候,可能会进行删减和排序, volatile可以禁止指令重新排序, 所以也就是说, 在对于volatile修饰的变量进行操作的时候是根据代码的顺序进行执行. 并且volatile提供了内存的可见性, 每个线程对器进行操作后,其他线程都会感知到, 并且从主存中获取这个变量,而不是缓冲区, 保证了实时性.

评论 0

还没有评论,来说点什么吧。

提交后需要审核才会公开。