单例模式初学

7 篇文章 0 订阅

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

该模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

感觉类似于全局变量,使用了单例模式,就不用频繁的创建与销毁这个类了,比如与数据库的连接等,线程池等一般就是设计 成单例。

使用时有以下注意点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

使用方法

主要是通过以下两个步骤:

将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;

在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。

如果唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。

所以就需要依据是否已经实例化的变量提供一个互斥锁,一般是synchronized (虽然这样会降低效率)。

基本实现

实现的方式很多,比如最简单的:

// 饿汉式单例
public class Singlecase1 {
    // 私有构造
    private Singlecase1() {}

    private static Singlecase1 single = new Singlecase1();

    public static Singlecase1 getInstance() {
        return single;
    }
}

该方式简单粗暴有效,就是在类装载的时候就完成实例化,避免了线程同步问题。

不过该方式没有办法保证线程安全,所以最直接想到的就是加锁,如下

//懒汉式
public class Singlecase2 {
    // 私有构造
    private Singlecase2() {}

    private static Singlecase2 single = null;

    public static Singlecase2 getInstance() {
        
        // 等同于 synchronized public static Singlecase2 getInstance()
        synchronized(Singlecase2.class){
          // 注意:里面的判断是一定要加的,否则出现线程安全问题
            if(single == null){
                single = new Singlecase2();
            }
        }
        return single;
    }
}

但这样也不好,此方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。

接下来就有了常用的比较推荐的方式,双重检查和静态内部类。

//进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
//实例化代码只用执行一次,后面再次访问时,判断if (single == null),直接return实例化对象。
//优点:线程安全;延迟加载;效率较高。
public class Singlecase3 {
    // 私有构造
    private Singlecase3() {}

    private static Singlecase3 single = null;

    // 双重检查
    public static Singlecase3 getInstance() {
        if (single == null) {
            synchronized (Singlecase3.class) {
                if (single == null) {
                    single = new Singlecase3();
                }
            }
        }
        return single;
    }
}

Synchronize关键字理解

synchronize就是同步锁,用来保证多线程运行时能够有序执行,可以用来修饰代码块(大括号括起来的),方法,静态方法,类。被修饰的方法或代码块就可以看成是一个原子操作了。

而这里修饰方法和代码块的,就是用的synchronize(this),即对象锁,修饰静态方法和类的,就是synchronize(xxx.class),即类锁。

区别就在于this和xxx.class是两把不同的锁,获取了class,this的同步对象不会受影响而去阻塞,反之亦然。
this锁住的是对象,两个线程里修饰的方法如果是同一实例对象,就可以同步,但如果不同就起不到作用了。
class锁住的是类,不同线程创建同一个类的不同实例或同一实例,那是都可以同步的。

//静态内部类方式在Singlecase4类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载InnerObject类,从而完成Singlecase4的实例化。
//类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
//优点:避免了线程不安全,延迟加载,效率高。
public class Singlecase4 {
    // 私有构造
    private Singlecase4() {}

    // 静态内部类
    private static class InnerObject{
        private static Singlecase4 single = new Singlecase4();
    }
    
    public static Singlecase4 getInstance() {
        return InnerObject.single;
    }
}

内部类与static

  • 内部类

顾名思义,在一个类的内部定义的另一个类,有四种:成员内部类,局部内部类,匿名内部类和静态内部类。像我们常用的事件监听如onclick,就用到了匿名内部类。

new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
            }
        }

对于其生命周期,与普通的对象一样,始于被创建时,终于系统回收。换句话说,当创建一个内部类之后,只有当没有人去引用它时,它才会被系统回收,从而死亡。

static成员变量生命周期与类一致。java类加载时会先加载类,再执行static变量的初始化,然后是对象的创建。所以static成员变量的生命周期是长于对象的,因为它可以在没有创建对象时使用,static变量只会在类初次加载时被初始化一次,然后保存在内存中,被所有对象共享,直到程序执行完,要回收类时,这时类的对象要回收,static成员变量也不例外,所以简单的说Static成员变量的生命周期与类的生命周期是一致的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值