单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案——摘自“百度百科”。
现在我们来看下singleton的实现。它的实现由多种方式,先来看下饿汉式:
package com.json.study.singleton;
/**
* 单例模式-饿汉式
*
* @note 线程安全,效率相对低
* @note 这方式是在服务启动时就创建对象,如果我有很多这样的单例,但是又有一些没有用到,那么就会出现占用资源的现象
* @author FuQiang
*/
public class Singleton {
// 服务启动时就加载
private static Singleton singleton = new Singleton();
// 构造器私有
private Singleton() {}
// 对外提供获取对象的方法
public static Singleton getInstance() {
return singleton;
}
}
饿汉式用的不多,下面我们介绍下另外一种实现方式——懒汉式。
package com.json.study.singleton;
/**
* 单例模式-懒汉式
*
* @note 线程不安全
* @author FuQiang
*/
public class Singleton1 {
private static Singleton1 singleton = null;
private Singleton1() {}
/**
* @eye 当第一个线程运行到if语句时发生了死锁,那么这个线程的 singleton==null
* @eye 当第二个线程运行到if语句时也发生了死锁,这个时候第一个线程没有创建实例,所以这个线程的singleton也为null
* @eye 这时候第一线程解除了死锁,运行进入判读条件生成singleton,第二个线程也解锁了,那么运行就会再生成一个singleton,
* @eye 而我们需要的是单例,这样就生成了多个,这是我们不允许的。
*/
public static Singleton1 getInstance() {
if (singleton == null) {
singleton = new Singleton1();
}
return singleton;
}
}
懒汉式是在用户需要的时候进行加载,就解决了饿汉式的资源浪费问题。但是这种方式会用线程安全的问题。所以我们需要解决这个问题,当然我们的第一反应就是同步。
package com.json.study.singleton;
/**
* 单例模式-懒汉式
*
* @note 线程安全,但是效率低
* @author FuQiang
*/
public class Singleton2 {
private static Singleton2 singleton = null;
private Singleton2() {}
/**
* @eye 每个线程都同步,效率太低,只要创建了实例,其他的就不用同步了
* @return
*/
public synchronized static Singleton2 getInstance() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
}
同步之后我们就达到了线程安全,但是这样效率方面会用问题,因为我们对每个线程都同步了。其实我们只需要在创建第一个实例的时候需要同步,当实例一旦创建后我们就不需要进行同步了。这就出现了我们的下面写法——双重检验锁(double checked lock):
package com.json.study.singleton;
/**
* 单例模式-懒汉式
* @note 线程安全
* @author FuQiang
*/
public class Singleton3 {
// 利用 volatile关键字,可提高效率
private volatile static Singleton3 singleton = null;
private Singleton3() {}
/**
* @eye 如果多个线程在第一个if语句发生了死锁,解锁后,会进行同步,所以线程安全,
* @eye 一旦生成了singleton实例,会在第一个if语句就跳出,解决了每个线程都同步的问题,提高了效率
*/
public static Singleton3 getInstance() {
if (singleton == null) {
synchronized (Singleton3.class){
if (singleton == null) {
singleton = new Singleton3();
}
}
}
return singleton;
}
}
上面的方式就解决了每个线程都要同步的问题,效率就提高了。但是代码有点多,其实也不多,但是相比于下面这种方式,有显得有点多了。下面这种方式,我们采用enum的方式,这是jdk1.5提出的概念,当然c,c++等编程语言是早就有这个概念的。
package com.json.study.singleton;
/**
* 枚举方式
* @author FuQiang
*/
public enum Singleton4 {
INSTANCE
}
枚举方式就是这么的简单。最后我们给出一些测试,仅供参考:
package com.json.study.singleton.test;
import org.junit.Test;
import com.json.study.singleton.Singleton;
import com.json.study.singleton.Singleton1;
import com.json.study.singleton.Singleton4;
public class SingletonTest {
@Test
public void testSingletion(){
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
@Test
public void testSingletion1(){
Singleton1 s1 = Singleton1.getInstance();
Singleton1 s2 = Singleton1.getInstance();
System.out.println(s1==s2);
}
@Test
public void testSingletion4(){
Singleton4 s1 = Singleton4.INSTANCE;
Singleton4 s2 = Singleton4.INSTANCE;
System.out.println(s1==s2);
}
}