单例模式是常用的设计模式之一,实质就是确保一个类只有一个实例对象,分为懒汉式和饿汉式两种。
类图如下:
一、懒汉式单例模式
1.懒汉式单例模式原始版:
开始设置实例对象为空,只要对象为空的话就返回一个实例对象。
2.懒汉式单例模式优化版(同步方法方式或同步块方式):
然而如果一个线程进入之后还没有将实例对象赋值之前,又有一个线程进入,这样就会先后创建两个对象,造成多个线程共同抢占同一份资源造成的同步问题。
为了解决这个问题,我们有两种方法:1.使用同步方法2.使用同步块,这两种方法都需要用到synchronized关键字。这样就会使得线程到来的时候只能一个一个的进入,保证了线程的安全。使用同步块的好处在于可以锁定只同步的代码部分,这样会比使用同步方法效率高。
3.懒汉式单例模式最终优化版:
然而在保证了线程安全的同时,又出现了另外一个问题就是由于同步的出现多个线程必须等待前一个线程出来之后自己才能进入,这样会会使得程序运行的效率低下。
为了解决这个问题,我们可以预先进行对象的判断,如果为空的话就进入,不为空就直接返回实例对象,而不进入同步的代码部分,从而提高了程序运行的效率。
二、饿汉式单例模式
1.饿汉式单例模式原始版:
开始就赋予一个实例对象,直接这个实例对象。
因为不存在调用方法创建实例对象的问题,也就不会存在多个线程进入方法中出现先后创建两个对象的问题,所以饿汉式本身就是线程安全的。
2.饿汉式单例模式最终优化版:
然而加载类的同时必然会加载它的属性,加入我需要使用这个单例模式得到实例对象,有可能在这个类中有其他方法要使用就要加载这个类,必然会使得加载类的同时加载了它的属性,这样会导致效率低下。
为了解决这个问题,我们将实例对象的定义放在一个内部类中,对于类而言,类只在使用的时候才会被加载,加载一个类,它的内部类不会被加载,调用得到实例方法的时候内部类才会加载,这样就延缓了加载时机,提高了程序运行的效率。
下面将详细代码分享如下:
懒汉式单例模式原始版:
class Jvm{
//声明一个私有的静态变量
private static Jvm instance=null;//懒得去创建对象,当使用的时候再去创建,所以是懒汉式
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
//创建一个对外的公共的静态方法访问变量,如果变量没有对象,创建该对象
public static Jvm getInstance(long time){
if(null==instance){
instance=new Jvm();
}
return instance;
}
懒汉式单例模式优化版(同步方法方式):
class Jvm{
//声明一个私有的静态变量
private static Jvm instance=null;//懒得去创建对象,当使用的时候再去创建,所以是懒汉式
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
//创建一个对外的公共的静态方法访问变量,如果变量没有对象,创建该对象
// 任何线程来的时候都需要等待,等上一个执行完了以后才能进去
public synchronized static Jvm getInstance(){
if(null==instance){
instance=new Jvm();
}
return instance;
}
}
懒汉式单例模式优化版(同步块方式):
class Jvm{
//声明一个私有的静态变量
private static Jvm instance=null;//懒得去创建对象,当使用的时候再去创建,所以是懒汉式
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
//创建一个对外的公共的静态方法访问变量,如果变量没有对象,创建该对象
// 任何线程来的时候都需要等待,等上一个执行完了以后才能进去
public static Jvm getInstance(){
// a b 效率不高 已经创建了对象也需要等待 都需要执行这段代码
synchronized (Jvm.class) { //同步块 因为静态方法中没有this,所以只能锁它的字节码信息
if(null==instance){
instance=new Jvm();
}
return instance;
}
}
}
懒汉式单例模式最终优化版:
class Jvm{
//声明一个私有的静态变量
private static Jvm instance=null;//懒得去创建对象,当使用的时候再去创建,所以是懒汉式
//构造器私有化,避免外部直接创建对象
private Jvm(){
}
public static Jvm getInstance(){
// 这样判断两次,可以提高效率 因为a、b进来之后,a执行完,b进入的时候如果instance不为空的话直接返回获取对象
// 提高已存在对象的访问效率
if (null==instance) {
synchronized (Jvm.class) { //同步块 因为静态方法中没有this,所以只能锁它的字节码信息
//每一个类都在JVM中有一个模板 把模子锁住了,不能制作玩具了
if(null==instance){
instance=new Jvm();
}
}
}
return instance;
}
}
饿汉式单例模式原始版:
class Jvm {
private static Jvm instance=new Jvm();//饿了就要吃东西 只要加载这个类,这个属性就会初始化
//有可能有其他方法要使用就要加载这个类,同时初始化了这个属性
private Jvm() {
}
public static Jvm getInstance(){//饿汉式本身就是线程安全的
return instance;
}
}
饿汉式单例模式最终优化版:
class Jvm {
private static class JVMholder{//内部类
private static Jvm instance=new Jvm();//饿了就要吃东西
}
private Jvm() {
}
public static Jvm getInstance(){//饿汉式本身就是线程安全的
return JVMholder.instance;
}
}