1、概念
程序在运行时,通常都会生成很多实例。但当我们想在程序中表达某个东西只会存在一个时,就会有只创建一个实例的需要。那就要在写程序时,确保调用一次new MyClass()
以达到只生成一个实例的目的。
像这样确保只生成一个实例的模式被称作Singleton模式(单例模式)。
Singleton模式的作用在于可以确保任何情况下都只能产生一个实例。为了达到这个目的,必须设置构造函数为private
创建一个单例类Singleton
, 然后创建一个测试类SingletonTest
对Singleton
进行判断。
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {
System.out.println("生成一个实例");
}
public static Singleton getInstance() {
return singleton;
}
}
public class SingletonTest {
public static void main(String[] args) {
System.out.println("开始");
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
if(singleton1 == singleton2) {
System.out.println("singleton1和singleton2为相同实例");
}else {
System.out.println("singleton1和singleton2为不同实例");
}
System.out.println("结束");
}
}
运行测试类得到的结果是:
开始
生成一个实例
singleton1和singleton2为相同实例
结束
2、不同实现
1、懒汉式(线程不安全)
通过提供一个静态的对象instance
,利用private
权限的构造方法和getInstance()方法来给予访问者一个单例。
缺点是没有考虑到线程安全,如果多个线程能够同时访问 if (instance == null)
,并且此时 instance
为 null
,那么多个线程会执行 instance = new Singleton()
。
之所以叫做懒汉模式,主要是因为此种方法可以非常明显的lazy loading(懒加载),即私有静态变量 instance 被延迟化实例化,这样做的好处如果没有用到该类,那么就不会实例化 instance,从而节约资源。
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
那怎么解决懒汉模式线程不安全的问题呢?首先想到就是在getInstance()方法前加锁,下面就是线程安全懒汉式。
2、懒汉式(线程安全)
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
当一个线程进入该方法之后,其它线程试图进入该方法都必须等待,所以大多时候这个锁占用的额外资源都浪费了,因此对性能有一定的损耗
3、饿汉式(线程安全)
正如开始介绍的第一个例子,其实就是一个饿汉式的单例模式。
没有懒加载的效果,所以会降低内存的使用率
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
4、双重校验锁(线程安全)
public class Singleton {
private volatile static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
双重校验锁即实现了懒加载,又解决了线程并发的问题,执行效率也得以保证
5、枚举方法(线程安全)
public enum Singleton {
INSTANCE;
public void oneMethod(){
System.out.println("Singleton");
}
}
使用枚举单例模式,可以很好的实现实例创建线程安全。防止了被反射创建多个实例,和序列化的问题。