单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。它分为懒汉式和饿汉式。
注意:
构造器必须私有化。对外必须获得一个公有的访问方式来获得实例。顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。
public class Teacher { private static Teacher t=new Teacher(); private Teacher() { } public static Teacher Get() { return t; } }优点:写法简单,在类加载的时候就完成实例化,避免线程同步。
缺点:没有达到懒加载的效果,若果自始至终都没有用过这个对象,就会造成内存浪费。
当程序第一次访问单例模式实例时才进行创建(延迟加载)。
缺点:只能在单线程下使用,多线程下会产生多个对象。
缺点:加锁进行同步,虽然可以保证单例,但效率太低,浪费大量时间。
著名的双重检查机制
public class Singleton3 { private volatile static Singleton3 singleton; //private static Singleton singleton; private Singleton3() { } public static Singleton3 getInstance() { if(singleton==null) { synchronized (Singleton3.class) { if(singleton==null) { singleton=new Singleton3(); } } } return singleton; } }优点:保证单例的同时,也提高了效率。
注意:singleton前面要加volatile关键字来保证程序运行的有序性,否则多线程访问下可能会出现对象未初始化错误!
说明:在内存中,创建一个变量需要三步:1.申请一块内存 ;2.调用构造方法初始化 ;3 分配一个指针指向这块内存
在编译原理中,有一个重要的内容叫做编译器优化,即在不改变原来语义的情况下,调整语句的执行顺序,来让程序运行的更快
因此存在这样一种情况,有两个线程A、B同时访问getInstance方法
A线程判断对象为空,没来得及进行第二次判断,(时间片用完了,B线程进入)B线程判断对象为空,执行创建变量的3步,先申请一块内存,后分配一个指针指向这块内存,但还没有进行初始化(时间片用完了,A线程进入)A线程接着执行,发现此时singleton已经不为空了,所以直接返回,但此时返回的singleton对象虽然B线程已经new了,但还没有初始化这个实例并没有构造完成,此时如果A线程使用这个实例,程序就会出现对象未初始化错误了。这里采用了静态内部类实例singleton对象,静态内部类相当于一个静态属性,只有在第一次加载类时才会初始化,在类初始化时,别的线程是无法进入的,因此保证了线程安全。