单例模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。单例模式有以下几种实现方式
- 饿汉方式
- 懒汉式—线程不安全:
- 懒汉式—线程安全:
- 双检锁式
- 登记式/静态内部类
- 枚举
饿汉式
将构造方法设置成私有方法,其他类无法new对象,从而保证只有一个实例。类加载到内存后,就实例化一个单例,JVM保证线程安全
- 优点:简单实用(推荐使用)
- 唯一缺点:不管是否用到,类加载时就会完成实例化对象
public class HungrySingleton {
private final static HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
HungrySingleton hs1 = HungrySingleton.getInstance();
HungrySingleton hs2 = HungrySingleton.getInstance();
System.out.println(hs1==hs2);
}
}
饿汉式的另一种写法
public class HungrySingleton{
private final static HungrySingletonTwo INSTANCE;
static {
INSTANCE = new HungrySingletonTwo();
}
private HungrySingleton(){}
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
懒汉式—线程不安全
优点:需要用到实例才初始化实例 缺点:带来了线程不安全问题
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance() {
if (instance == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) { e.printStackTrace(); }
instance = new LazySingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(LazySingleton.getInstance().hashCode());
}).start();
}
}
}
懒汉式—线程安全
懒汉式改进版(不推荐),通过Synchronized来解决线程不安全问题 缺点:虽然解决了线程安全问题,但是执行效率会降低
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
双检锁式
懒汉式改进版,这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class){
if (instance == null){
instance = new LazySingleton_4();
}
}
}
return instance;
}
登记式/静态内部类
静态内部类方式(完美写法之一)JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载,但是这种单例模式可以被反射机制和反序列化破坏
public class InnerSingleton implements Serializable {
private InnerSingleton(){}
private static class InnerSingletonHolder{
private final static InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance() {
return InnerSingletonHolder.instance;
}
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(InnerSingleton.getInstance().hashCode());
}).start();
}
}
}
反射机制破坏单例模式解决方法
反射机制破坏单例模式代码
public class DestroyTest {
public static void main(String[] args) {
try {
Class mgr = Class.forName("InnerSingleton");
Constructor declaredConstructor = mgr.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerSingleton m1 = (InnerSingleton)declaredConstructor.newInstance();
InnerSingleton m2 = (InnerSingleton)declaredConstructor.newInstance();
InnerSingleton m3 = (InnerSingleton)declaredConstructor.newInstance();
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在InnerSingleton中判断是否已经存在了一个InnerSingleton对象,如果没有存在就new一个对象,如果已经存在就向外抛出异常
public static boolean isExist = false;
private InnerSingleton(){
synchronized(InnerSingleton.class) {
if (isExist) {
throw new RuntimeException("不能创建多个对象");
} else {
isExist = true;
}
}
}
反序列化破坏单例模式解决方法
反序列化破坏单例模式代码
public class DestroyTest {
public static void main(String[] args) {
try {
writeToFile();
readObj();
readObj();
}catch (Exception e) {
e.printStackTrace();
}
}
public static void writeToFile() throws IOException {
InnerSingleton mgr = InnerSingleton.getInstance();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\a.txt"));
out.writeObject(mgr);
out.close();
}
public static void readObj() throws Exception{
ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\a.txt"));
InnerSingleton mgr = (InnerSingleton)in.readObject();
System.out.println(mgr);
in.close();
}
}
在InnerSingleton中添加一个readResolve()方法,该方法在序列化时被反射机制调用, 如果定义了这一个方法就返回这个方法的返回值,否则就返回新new出来的对象
public Object readResolve(){
return InnerSingletonHolder.instance;
}
枚举
枚举单例(完美写法之一)不仅可以保证单例,还可以防止序列化,java的反射机制可以通过Class文件newInstance反序列化一个对象,枚举类不会被反序列化的原因是枚举类没有构造方法
public enum EnumSingleton {
INSTANCE;
public void BusinessMethod(){ }
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(EnumSingleton.INSTANCE.hashCode());
}).start();
}
}
}
|