单例模式讲解

单例模式:
1.手写单例模式
2.线程安全的单例模式

什么是单例
单例类在整个程序中只能有一个实例,这个类负责创建自己的对象,并确保只有一个对象创建。
比如:数据库连接池

饿汉式:线程安全,反射和序列化不安全
package com.feicheng.bean;
/**

  • 饿汉式

  • @author Lenovo
    */
    public class Singleton1 {

    /**

    • 私有静态对象
      */
      private static final Singleton1 instance = new Singleton1();

    /**

    • 私有构造器
      */
      private Singleton1(){}

    /**

    • 对外提供获取实例的方法

    • @return
      */
      public static Singleton1 getInstance(){

      return instance;
      }

    private Object readResolve(){

      return instance;
    

    }

}
代码实现要点:
a)私有构造器
b)持有该类的属性
c)对外提供获取实例的静态方法

优点:我们在获取对象的过程中,获取到的是一个对象,线程安全
/**

  • 使用线程获取
    */
    @Test
    public void test2(){

    for (int i = 0; i <20; i++) {

     new Thread(new Runnable() {
         @Override
         public void run() {
    
             System.out.println(Singleton1.getInstance());
         }
     }).start();
    

    }
    }
    缺点:在反射和反序列化中是不安全的
    /**

  • 对反射来讲,是不安全的

  • @throws Exception
    */
    @Test
    public void test3() throws Exception {

    Class clazz = Singleton1.class;

    Constructor constructor = clazz.getDeclaredConstructor();

    constructor.setAccessible(true);

    Singleton1 singleton1 = Singleton1.getInstance();

    Singleton1 singleton2 = (Singleton1) constructor.newInstance();

    System.out.println(singleton1 == singleton2);
    }
    在序列化中需要加上
    private Object readResolve(){

    return instance;
    }

登记式:线程安全,是一个加强版的饿汉式,防止反射攻击,反序列化安全,支持反序列化
package com.feicheng.bean;

/**

  • 登记式

  • @author Lenovo
    */
    public class Singleton2 {

    /**

    • 私有静态内部类
      */
      private static class SingletonHolder{

      private static Singleton2 instance = new Singleton2();
      }

    /**

    • 私有构造器
      */
      private Singleton2(){

      System.out.println(“Singleton reload”);

      if (SingletonHolder.instance != null){

       throw new IllegalStateException();
      

      }
      }

    /**

    • 对外提供获取实例的方法

    • @return
      */
      public static Singleton2 getInstance(){

      return SingletonHolder.instance;
      }
      }
      测试方法:
      package com.feicheng.episode;

import com.feicheng.bean.Singleton1;
import com.feicheng.bean.Singleton2;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Singleton2Test {

@Test
public void test1(){

// Singleton2 singleton1 = Singleton2.getInstance();
//
// Singleton2 singleton2 = Singleton2.getInstance();
//
// System.out.println(singleton1 == singleton2);

    /**
     * 这样不会加载构造方法
     */
    try {
        Class.forName("com.feicheng.bean.Singleton2");

        // 只有加上这个才在加载构造方法
        Singleton2.getInstance();

    } catch (ClassNotFoundException e) {

        e.printStackTrace();
    }
}


/**
 * 使用反射,也是不安全的
 */
@Test
public void  test2() throws Exception {

   Class clazz = Singleton2.class;

   Constructor constructor = clazz.getDeclaredConstructor();

   constructor.setAccessible(true);

   Singleton2 singleton1 = Singleton2.getInstance();

   Singleton2 singleton2 = (Singleton2) constructor.newInstance();

    System.out.println(singleton1 == singleton2);
}
/**
 * 对反射来讲,是不安全的,
 * @throws Exception
 */
@Test
public void test3() throws Exception {

    Class clazz = Singleton1.class;

    Constructor constructor = clazz.getDeclaredConstructor();

    constructor.setAccessible(true);

    Singleton1 singleton1 = Singleton1.getInstance();

    Singleton1 singleton2 = (Singleton1) constructor.newInstance();

    System.out.println(singleton1 == singleton2);

}

}
要想反射安全需要在类里面构造方法加上:
/**

  • 私有构造器
    */
    private Singleton2(){

    System.out.println(“Singleton reload”);

    if (SingletonHolder.instance != null){
    throw new IllegalStateException();
    }
    }

枚举式:线程安全,防止反射攻击
package com.feicheng.bean;

/**

  • 枚举式

  • @author Lenovo
    */
    public enum Singleton3 {

    INSTANCE{
    @Override
    protected void doSomeThing() {

          System.out.println("doSomeThing");
      }
    

    };

    protected abstract void doSomeThing();

}

懒汉式:线程不安全,延迟加载,(两种加同步,效率低)
双检索:线程安全,需要在类上添加Volatile

package com.feicheng.bean;

/**

  • 懒汉式

  • @author Lenovo
    */
    public class Singleton4 {

    /**

    • 私有静态对象
      */
      private static volatile Singleton4 instance = null;

    /**

    • 私有构造器
      */
      private Singleton4(){}

    /**

    • 对外提供获取实例的方法

    • @return
      */
      public static Singleton4 getInstance(){

      if (instance == null){

       return new Singleton4();
      

      }

      return instance;
      }

    // 若要使用懒汉式线程安全,则需要将构造方法重写成这样
    /**

    • 使用双检索的形式
    • @return
      */
      public static Singleton4 getInstanceSy(){
      if (instance == null){
      synchronized (Singleton4.class){
      if (instance == null){
      return new Singleton4();
      }
      }
      }
      return instance;
      }
      // 但这种方式是是不是100%线程安全呢?需要在对象前面加一个Volatile,这样就不会执行指令重排
      }
      若执行instance = new Singleton4()会执行如下操作
      (1)分配对象内存空间
      (2)初始化对象
      (3)instance指向(1)中分配的空间
      在某些编译器上,可能会出现指令重排:
      (1)分配对象内存空间
      (2)instance指向(1)中分配的空间(但此时对象没有被初始化)
      (3)初始化对象

ThreadLocal:在一个线程里面是单列的,但在不同的线程里面是多列的
不加锁,以空间换时间,为每个线程提供变量的独立副本,可以保证各自线程中是单例的,但不同线程中不保证
package com.feicheng.bean;

/**

  • ThreadLocal

  • @author Lenovo
    */
    public class Singleton5 {

    private static Singleton5 instance = null;

    private Singleton5(){}

    private static final ThreadLocal < Singleton5> threadLocal = new ThreadLocal < Singleton5>(){
    @Override
    protected Singleton5 initialValue() {

          return new Singleton5();
      }
    

    };

    public static Singleton5 getInstance(){

      return threadLocal.get();
    

    }
    }

测试方法:
package com.feicheng.episode;

import com.feicheng.bean.Singleton4;
import com.feicheng.bean.Singleton5;
import org.junit.Test;
import java.io.IOException;

public class Singleton5Test {

@Test
public void test1(){

    Singleton5 singleton1 = Singleton5.getInstance();

    Singleton5 singleton2 = Singleton5.getInstance();

    System.out.println(singleton1 == singleton2);
}

@Test
public void test2(){

    for (int i = 0; i < 20; i++) {

        new Thread(new Runnable() {

            @Override
            public void run() {

                Singleton5 singleton1 = Singleton5.getInstance();

                Singleton5 singleton2 = Singleton5.getInstance();

                System.out.println(Thread.currentThread().getName() + "----" + (singleton1 == singleton2) + "---" + singleton1 + "," + singleton2);
            }
        }).start();
    }

    try {

        System.in.read();

    } catch (IOException e) {

        e.printStackTrace();
    }
}

}

CAS:无锁乐观策略,线程安全
package com.feicheng.bean;

import java.util.concurrent.atomic.AtomicReference;

/**

  • CAS

  • @author Lenovo
    */
    public class Singleton6 {

    /**

    • 对象引用,进行原子类封装
      */
      private static final AtomicReference instance = new AtomicReference < Singleton6>();

    /**

    • 私有化构造方法
      */
      private Singleton6(){

      System.out.println(“Singleton6”);
      };

    public static final Singleton6 getInstance() {

      for (; ; ) {
          Singleton6 singleton6 = instance.get();
    
          if (singleton6 != null) {
    
              return singleton6;
          }
    
          singleton6 = new Singleton6();
    
          if (instance.compareAndSet(null, singleton6)) {
    
              return singleton6;
          }
      }
    

    }
    }
    测试方法:
    package com.feicheng.episode;

import com.feicheng.bean.Singleton5;
import com.feicheng.bean.Singleton6;
import org.junit.Test;
import java.io.IOException;

public class Singleton6Test {

@Test
public void test1(){

    for (int i = 0; i < 20; i++) {
        new Thread(new Runnable() {

            @Override
            public void run() {

               System.out.println(Singleton6.getInstance());
            }
        }).start();
    }

    try {
        System.in.read();

    } catch (IOException e) {

        e.printStackTrace();
    }
}

}

总结:
*饿汉式:线程安全、反射不安全、反序列化不安全
登记式:线程安全、防止反射攻击、反序列化不安全
枚举式:线程安全、支持反序列化、反序列化安全、防止反射攻击
懒汉式:线程不安全、延迟加载、(两种加同步,效率低)
* 双检锁:线程安全、volatile
ThreadLocal:不加锁,以空间换时间,为每个线程提供变量独立的副本,可以保证各自线程中是单例的,但是不同线程之间不保证
CAS:无锁乐观策略,线程安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值