其目的在于一个类在任何情况下只有一个实例
1.饿汉式单例
类在被加载的时候初始化
public class HungrySingleton {
private static final HungrySingleton hungrySigleton = new HungrySingleton();
//将单例对象的构造器私有化,无法直接通过 new 来创建对象,只能通过我们提供的 getInstance 方法来获得单例对象
private HungrySingleton() {
}
public static HungrySingleton getInstance(){
return hungrySigleton;
}
}
//第二种 静态代码块的方式
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySigleton;
static {
hungrySigleton = new HungryStaticSingleton();
}
private HungryStaticSingleton() {
}
public static HungryStaticSingleton getInstance(){
return hungrySigleton;
}
}
优点:
创建对象没有加锁、执行效率比较高
缺点
浪费内存
2.懒汉式单例
被使用时才会初始化
//第一种,线程不安全
public class LazySingleton {
//开始只是赋了一个 null 值,并没有进行初始化,调用 getInstance 方法的时候才会去初始化单例对象
private static LazySingleton lazySingleton = null;
private LazySingleton() {
}
public static LazySingleton getInstance(){
if(null == lazySingleton){//为空则说明第一次获取单例对象,进行初始化
lazySingleton = new LazySingleton();
}
return lazySingleton;//不为空则说明已经初始化了,直接返回
}
}
//第二种,线程安全,加锁
public class LazySyncSingleton {
private static LazySyncSingleton lazySingleton = null;
private LazySyncSingleton() {
}
public synchronized static LazySyncSingleton getInstance(){
if(null == lazySingleton){
lazySingleton = new LazySyncSingleton();
}
return lazySingleton;
}
}
//第三种,线程安全,双空判断(双重锁)
//一个是 lazySingleton 属性上加了 volatile 关键字来修饰,原因就是解决多线程下的可见性问题,因为我们的 getInstance 方法在判断 lazySingleton是否为 null 时候并没有加锁,所以假如线程 t1 初始化过了对象,另外线程如 t2 是无法感知的,而加上了 volatile 就可以感知到。同时volatile 可以禁止指令重排序
//另一个改变就是把 synchronized 关键字移到了方法内部,尽可能缩小加锁的代码块,提升效率
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazySingleton = null;
private LazyDoubleCheckSingleton() {
}
public static LazyDoubleCheckSingleton getInstance(){
if(null == lazySingleton){
synchronized (LazyDoubleCheckSingleton.class){
if(null == lazySingleton){
lazySingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazySingleton;
}
}
//第四种 内部懒汉,不过不能防反射
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){
}
public static final LazyInnerClassSingleton getInstance(){
return InnerLazy.LAZY;
}
private static class InnerLazy{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
//第五种 防止反射破坏的内部懒汉,不能防序列化
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){
//防止反射破坏单例
if(null != InnerLazy.LAZY){
throw new RuntimeException("不允许通过反射类构造单例对象");
}
}
public static final LazyInnerClassSingleton getInstance(){
return InnerLazy.LAZY;
}
private static class InnerLazy{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
//第六种 防止反射破坏,防止序列化破坏的内部懒汉
public class LazyInnerClassSingleton implements Serializable {
private LazyInnerClassSingleton(){
//防止反射破坏单例
if(null != InnerLazy.LAZY){
throw new RuntimeException("不允许通过反射类构造单例对象");
}
}
public static final LazyInnerClassSingleton getInstance(){
return InnerLazy.LAZY;
}
private static class InnerLazy {
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
private Object readResolve(){
return InnerLazy.LAZY;
}
}
3.注册式单例
将每一个实例都保存起来,然后在需要使用的时候直接通过唯一的标识获取实例
public class ContainerSingleton {
private ContainerSingleton(){
}
private static Map<String,Object> ioc = new ConcurrentHashMap<>();//存储单例对象
public static Object getBean(String className){
synchronized (ioc){
if(!ioc.containsKey(className)){//如果容器中不存在当前对象
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className,obj);//将className作为唯一标识存入容器
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
return ioc.get(className);//如果容器中已经存在了单例对象,则直接返回
}
}
}
public class MyObject {
}
public class TestContainerSingleton {
public static void main(String[] args) {
MyObject myObject1 = (MyObject) ContainerSingleton.getBean("package.MyObject");
MyObject myObject2 = (MyObject) ContainerSingleton.getBean("package.MyObject");
System.out.println(myObject1 == myObject2);//输出:true // true 是因为我们加了 synchronized 关键字,实际上 Spring 框架中用的就是容器式单例,默认是线程不安全的。
}
}
4.枚举式
一种优雅的写法
public class MyObject {
}
public enum EnumSingleton {
INSTANCE;
private MyObject myObject;
EnumSingleton() {
this.myObject = new MyObject();
}
public Object getData() {
return myObject;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
public class TestEnumSingleton {
public static void main(String[] args) throws Exception{
EnumSingleton enumSingleton = EnumSingleton.getInstance();
System.out.println(enumSingleton.getData() == enumSingleton.getData());//输出:true
}
}