单例模式 (Singleton
) 是一种建立型模式,指某个类采用Singleton
模式,则在这个类被建立后,只可能产生一个实例供外部访问,而且提供一个全局的访问点。java
Java
中单例模式 (Singleton
) 是一种普遍使用的设计模式。单例模式的主要做用是保证在Java
程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。编程
简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new
,由于构造器是被private
修饰的,通常经过getInstance()
的方法来获取它们的实例。getInstance()
的返回值是一个同一个对象的引用,并非一个新的实例。单例模式 实现起来也很容易,如下给出六种实现方式:后端
特色:线程安全,没法实现实例懒加载策略。设计模式
public class Singleton1 {
private final static Singleton1 singleton1 = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return singleton1;
}
}
复制代码
特色:线程不安全,实现了实例懒加载策略。缓存
public class Singleton2 {
private final static Singleton2 singleton2;
private Singleton2() {
}
public static Singleton2 getInstance() {
if (singleton2 == null)
singleton2 = new Singleton2();
return singleton2;
}
}
复制代码
特色:线程安全,且实现了懒加载策略,可是线程同步时效率不高。安全
public class Singleton3 {
private final static Singleton3 singleton3;
private Singleton3() {
}
public synchronized static Singleton3 getInstance() {
if (singleton3 == null)
singleton3 = new Singleton3();
return singleton3;
}
}
复制代码
特色:线程安全,类主动加载时才初始化实例,实现了懒加载策略,且线程安全。多线程
public class Singleton4 {
private final static Singleton4 singleton4;
private Singleton4() {
}
static {
singleton4 = new Singleton4();
}
public static Singleton4 getInstance() {
return singleton4;
}
}
复制代码
特色:线程安全,且实现了懒加载策略,同时保证了线程同步时的效率。可是volatile
强制当前线程每次读操做进行时,保证全部其余的线程的写操做已完成。volatile
使得JVM
内部的编译器舍弃了编译时优化,对于性能有必定的影响。架构
public class Singleton5 {
private static volatile Singleton5 singleton5;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (singleton5 == null) {
synchronized (Singleton5.class) {
if (singleton5 == null) {
singleton5 = new Singleton5();
}
}
}
return singleton5;
}
}
复制代码
特色:线程安全,不存在线程同步问题,且单例对象在程序第一次 getInstance()
时主动加载 SingletonHolder
和其 静态成员 INSTANCE
,于是实现了懒加载策略。框架
public class Singleton6 {
private Singleton6() {
}
private static class SingletonHolder {
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() {
return Singleton6.SingletonHolder.INSTANCE;
}
}
复制代码
特色:线程安全,不存在线程同步问题,且单例对象在枚举类型 INSTANCE
第一次引用时经过枚举的 构造函数 初始化,于是实现了懒加载策略。异步
public class Singleton7 {
private Singleton7() {
}
enum SingletonEnum {
INSTANCE;
private final Singleton7 singleton7;
private SingletonEnum() {
singleton7 = new Singleton7();
}
}
public static Singleton7 getInstance() {
return SingletonEnum.INSTANCE.singleton7;
}
public static void main(String[] args) {
IntStream.rangeClosed(0, 100).forEach(i -> new Thread() {
public void run() {
out.println(Singleton7.getInstance());
};
}.start());
}
}
复制代码
这种方式是Effective Java
做者 Josh Bloch
提倡的方式,它不只能避免多线程同步问题,并且还能防止反序列化从新建立新的对象,可谓是很坚强的壁垒啊。不过,因为JDK 1.5
中才加入enum
特性,用这种方式写难免让人感受生疏。
测试代码以下:
@FixMethodOrder
public class SingletonTester {
protected final static int FROM = 0;
protected final static int TO = 1000;
protected static HashSet<Object> GLOBAL_SET = new HashSet<>();
static {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
out.println();
// count
GLOBAL_SET.forEach((value) -> {
out.println("Global [" + value + "]");
});
}
});
}
// testSingleton1
@Test
public void testSingleton1() throws Exception {
final HashSet<Object> localSet = new HashSet<>();
final CountDownLatch latch = new CountDownLatch(TO);
IntStream.range(FROM, TO).forEach(i -> new Thread() {
public void run() {
Singleton1 singleton = Singleton1.getInstance();
count(singleton);
}
protected void count(Singleton1 singleton) {
localSet.add(singleton);
out.println("Size of HashSet1 is: [" + localSet.size() + "]");
// 计数减1,释放线程
latch.countDown();
};
}.start());
// 等待子线程执行结束
latch.await();
synchronized (localSet) {
// count
localSet.forEach((value) -> {
out.println("[" + value + "]");
out.println();
});
GLOBAL_SET.addAll(localSet);
}
}
// testSingleton2
// testSingleton3
// testSingleton4
// testSingleton5
// testSingleton6
// testSingleton7
}
复制代码
测试结果截图以下,测试用例反映7
种单例模式的方案均可以正常执行:
这里只演示其中一种单例方式,运行截图以下:
getInstance()
获得的实例全局惟一。对于其他六中方式,根据测试用例测试获得的结果一致,你们能够自行测试。
本文总结了七种Java
中实现单例模式的方法,其中使用双重校验锁、静态内部类 和 枚举类 的方式能够解决大部分问题。其中,极为推荐 静态内部类 和 枚举类 这两种实现方式。
欢迎关注技术公众号: 零壹技术栈
本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。