Pattern1-Singleton
什么是单例模式
- 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。该设计为了保证在一个进程中,某个类有且仅有一个实例。
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
单例的实现方式有四种,下面分别进行介绍:
方式一 :饿汉式(简单易用)类加载到内存中时,就进行初始化。
public class Single01 {
//1.保证实例只能在类中进行实例化,其他类中不能进行实例化
private Single01(){}
//2.创建静态final实例
private static final Single01 INSTANCE = new Single01();
//3.提供其他对象访问实例的方法
public static Single01 getInstance(){
return INSTANCE;
}
//4.公共方法
public void test() {
System.out.println("single01");
}
//进行测试,看单例模式否是创建成功
public static void main(String[] args) {
Single01 single1 = new Single01();
Single01 single2 = new Single01();
System.out.println(single1 == single2);
//结果是true,表示两个创建的是一个对象。
}
}
- 优缺点
- 类加载到内存后,就实例化一个单例,JVM保证线程安全,
- 保证每一个class只会load内存一次,static是在class在load内存中以后
- 就开始实例化的,也只保证初始化一次。
- ★简单实用,推荐使用!
- 唯一缺点:不管用到与否,类装载时就完成实例化
方式二:懒汉式(lazy loading)
- 和第一种方式不同的是在需要的时候才会进行实例的创建
- 虽然实现了按需初始化的目的,但是却不是线程安全的,具体实现如下。
public class Single02 {
private Single02(){}
private static Single02 INSTANCE;
//按需进行实例化
public static Single02 getInstance(){
if(INSTANCE == null){
INSTANCE = new Single02();
}
return INSTANCE;
}
}
此时为什么说是线程不安全的呢?在进行getInstance()时,加入此时有两个线程都执行到了
if(INSTANCE == null){}
这个条件,而且都执行通过了,这时候就会创建两个实例对象,导致单例模式失败,线程不安全。
- 为了展示这一个线程不安全的状态,下面进行一个测试:
- 为了使测试效果更明显,我们让在getInstance时停顿一下,修改内容如下:
//按需进行实例化
public static Single02 getInstance(){
if(INSTANCE == null){
try {
//进程睡眠10毫秒
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
INSTANCE = new Single02();
}
return INSTANCE;
}
- 下面创建十个进程来同时常见单例对象,查看结果是否为单例。结果表明不是单例模式;
public static void main(String[] args) {
for(int i=0; i<10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Single02.getInstance());
}
}
).start();
}
}
结果:singleton.Single02@4a140fe5
singleton.Single02@49fb8056
singleton.Single02@9d30fed
singleton.Single02@2abb5f1c
singleton.Single02@692db0ef
singleton.Single02@7e92ee0
singleton.Single02@3fe9e503
singleton.Single02@12cddde4
singleton.Single02@6eb7e2c8
singleton.Single02@13f938d6
- 对于该方式,也可以通过加锁来实现线程安全;但是降低了系统的效率
public class Single02 {
private Single02(){}
private static Single02 INSTANCE;
//按需进行实例化
public static synchronized Single02 getInstance(){
if(INSTANCE == null){
INSTANCE = new Single02();
}
return INSTANCE;
}
}
★方式三:静态内部类方式(工作中常用方式)
- 对于此方式,JVM保证单例,JVM加载class时只加载一次,保证的单例
- 加载外部类时不会加载内部类,这样可以实现懒加载(按需加载)
public class Single04 {
private Single04(){}
//内部类实现懒加载
private static class Single04Holder{
private static final Single04 INSTANCE = new Single04();
}
public static Single04 getInstance(){
return Single04.Single04Holder.INSTANCE;
}
}
方式四:枚举单例(完美主义)
- Java创始人之一在《Effective Java》中提出使用枚举单例的方式,
- 不仅可以解决线程同步,还可以防止反序列化。
public enum Single05 {
INSTANCE;
}