我们可以在Java中打破单例模式的不同方法有哪些。 我知道一种方法,即如果我们不在单例中同步方法,那么我们可以创建多个类的实例。因此应用同步。有没有办法打破单例java类。
public class Singleton {
private static Singleton singleInstance;
private Singleton() {
}
public static Singleton getSingleInstance() {
if (singleInstance == null) {
synchronized (Singleton.class) {
if (singleInstance == null) {
singleInstance = new Singleton();
}
}
}
return singleInstance;
}
}
答案 0 :(得分:10)
从您给定的代码开始,“Double-Checked Locking”可能会在某些环境中被破坏, 在使用Symantec JIT的系统上运行时,它不起作用。特别是,Symantec JIT编译
singletons[i].reference = new Singleton();
以下内容(请注意Symantec JIT使用基于句柄的对象分配系统)。
0206106A mov eax,0F97E78h
0206106F call 01F6B210 ; allocate space for
; Singleton, return result in eax
02061074 mov dword ptr [ebp],eax ; EBP is &singletons[i].reference
; store the unconstructed object here.
02061077 mov ecx,dword ptr [eax] ; dereference the handle to
; get the raw pointer
02061079 mov dword ptr [ecx],100h ; Next 4 lines are
0206107F mov dword ptr [ecx+4],200h ; Singleton's inlined constructor
02061086 mov dword ptr [ecx+8],400h
0206108D mov dword ptr [ecx+0Ch],0F84030h
如您所见,在调用Singleton的构造函数之前执行了对singletons [i] .reference的赋值。这在现有的Java内存模型下是完全合法的,在C和C ++中也是合法的(因为它们都没有内存模型)。
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
除此之外
Serializable
Reflection
(我相信)* 您如何解决规则破坏者?
readResolve()
方法并抛出异常clone()
并抛出CloneNotSupported
例外示例强>
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// Check if we already have an instance
if (INSTANCE != null) {
throw new IllegalStateException("Singleton" +
" instance already created.");
}
}
public static final Singleton getInstance() {
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
private Object writeReplace() throws ObjectStreamException {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException {
// return INSTANCE
throw new CloneNotSupportedException();
}
}
毕竟我建议使用Enum作为Singleton最安全的方法(因为java5最好的方法是使用枚举)
public static enum SingletonFactory {
INSTANCE;
public static SingletonFactory getInstance() {
return INSTANCE;
}
}
答案 1 :(得分:3)
实际上,无需同步的安全版本为the version with a nested holder class:
public final class Singleton{
public static final Singleton getInstance(){
// no need for synchronization since the classloader guarantees to initialize
// Holder.INSTANCE exactly once before handing out a reference to it
return Holder.INSTANCE;
}
private Singleton();
private static class Holder{
private static final Singleton INSTANCE = new Singleton();
}
}
其他安全版本是:
public final class Singleton{
public static final Singleton getInstance(){
return INSTANCE;
}
private Singleton();
private static final Singleton INSTANCE = new Singleton();
}
public enum Singleton{
INSTANCE;
}
所有这些版本都有优点和缺点,但它们都不需要显式同步,因为它们都依赖于ClassLoader及其内置的Thread安全性。
正如其他人所写,您可以通过反序列化来打破其中的一些模式。阅读Joshua Bloch撰写的有效Java(第74至78项)关于防止此类攻击(枚举单例模式可以安全地防止此类攻击开箱即用)。
答案 2 :(得分:2)
一种方法是序列化。如果你没有实现readResolve,那么用ObjectInputStream.readObject()读取一个单例将返回这个单例的一个新实例。
答案 3 :(得分:1)
AFAIK,有两种方法可以打破
使用反思
当有自定义类加载器时,多个(即父类加载器)。所有单例应该由公共父类加载器加载。
答案 4 :(得分:1)
import java.io.Serializable;
public class Singleton implements Serializable,Cloneable{
private static final long serialVersionUID = 1L;
private static Singleton singleton=null;
private Singleton(){
}
public static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
**单身人士测试**
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/***
*
* Ways to break Singleton
*/
public class Main {
private static ObjectInputStream inputStream;
public static void main(String[] args) throws Exception {
Singleton orginalSingletonObject = Singleton.getInstance();
/***
* Singleton is broken by using Reflection
*/
breakSingletonByReflection(orginalSingletonObject);
/***
* By Serialization/De-Serialization break Singleton We need
* Serialization interface in a class nedds to be serialized like
* Singleton.java
*/
breakSingletonByserialization(orginalSingletonObject);
/***
* By Cloning break Singleton
* We need to implement Cloneable interface
*/
breakSingletonByCloning(orginalSingletonObject);
/***
* Break Singleton By thread
* This scenario is related to multi-threading environment
*
*/
breakSingletonByThreading(orginalSingletonObject);
}
private static void breakSingletonByThreading(Singleton orginalSingletonObject) {
ExecutorService executorService=Executors.newFixedThreadPool(2);
/**
* Run this code snippet after commenting the other code for better understanding
* Run it repeatly to create a condition when 2 threads enter the method getInstance() of Singleton class at a same time
* When 2 threads enter the getInstance method at same time they will get the singleton object as null (private static Singleton singleton in Singleton.java)
* Then they will create two different objects ( have different hashcode) in this case singleton pattern will break.
*/
executorService.submit(Main::useSingleton); // JAVA 8 syntax it will get the singleton instance
executorService.submit(Main::useSingleton);
executorService.shutdown();
}
public static void useSingleton(){
Singleton singleton=Singleton.getInstance();
printSingletonData("By Threading", singleton);
}
private static void breakSingletonByCloning(Singleton orginalSingletonObject) throws CloneNotSupportedException {
Singleton clonedSingletonObject=(Singleton) orginalSingletonObject.clone();
printSingletonData("By Cloning", orginalSingletonObject, clonedSingletonObject);
}
private static void breakSingletonByReflection(Singleton orginalsingleton)
throws ClassNotFoundException, NoSuchMethodException,
InstantiationException, IllegalAccessException,
InvocationTargetException {
Class<?> singletonClass = Class.forName("SingletonTest.Singleton");
@SuppressWarnings("unchecked")
Constructor<Singleton> constructor = (Constructor<Singleton>) singletonClass
.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s = constructor.newInstance();
printSingletonData("By Reflection", orginalsingleton, s);
}
private static void breakSingletonByserialization(Singleton orginalsingleton)
throws FileNotFoundException, IOException, ClassNotFoundException {
/**
* Serialization
*/
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("E:\\Singleton.ser"));
outputStream.writeObject(orginalsingleton);
outputStream.close();
/**
* DeSerialization
*/
inputStream = new ObjectInputStream(new FileInputStream("E:\\Singleton.ser"));
Singleton deserializeObject = (Singleton) inputStream.readObject();
deserializeObject.hashCode();
printSingletonData("By Serialization", orginalsingleton, deserializeObject);
}
public static void printSingletonData(String operationName,
Singleton orginalsingleton, Singleton reflectionSigletonObject) {
System.out.println("------------------------------------------");
System.out.println("New Operation");
System.out.println(operationName);
System.out.println("orginal Hashcode=" + orginalsingleton.hashCode());
System.out.println("New Object hashcode="
+ reflectionSigletonObject.hashCode());
Boolean value = orginalsingleton.hashCode() != reflectionSigletonObject.hashCode();
System.out.println("These Object have different hascode. They are two different object Right = "
+ value);
System.out.println("As these are different Object this means Singleton Pattern is broken");
}
private static void printSingletonData(String operationName,Singleton singleton) {
System.out.println("------------------------------------------");
System.out.println("New Operation");
System.out.println(operationName);
System.out.println("Object hashcode=" + singleton.hashCode());
}
}
答案 5 :(得分:1)
Class cls = Singleton.class;
Constructor constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton = (Singleton) constructor.newInstance();
答案 6 :(得分:0)
多线程是单身人士面临的最大问题。 您可以通过同步或使用急切初始化来避免这种情况。
另一种方法是使用你不应该使用的单身人士。通过使用单例模式,您可以在以后的开发中以一种阻碍程序的方式应用它。 (E.G,在游戏中创建单身“玩家”,因为你认为它是单人游戏。开发的下一步是“添加合作功能”)。
单身模式有它的优点,但如果不仔细考虑就不要使用它。
答案 7 :(得分:0)
首先注意:在这种情况下,主要是为了提高可读性,可以更好地制作整个getSingleInstance() { }
synchronized
:
public synchronized static Singleton getSingleInstance() {
if (singleInstance == null) {
singleInstance = new Singleton();
}
return singleInstance;
}
除此之外,我认为打破代码并不容易。当然,如果你添加递归调用,那么可以打破它,如下所示:
Singleton
的构造函数中调用其他类的构造函数。Singleton
实例。但这是我唯一可以考虑的事情,你无法在Singleton
课程中保护自己。
答案 8 :(得分:0)
同步方法将起作用,但也会降低对单例的每次访问速度,以保护只在第一次访问时发生的事情。
最简单和最安全的方法就是进行急切的初始化,这始终是安全的,因为Java保证在允许任何人访问它们之前设置所有成员变量。
public class Singleton {
private static Singleton singleInstance = new Singleton();
private Singleton() {
}
public static Singleton getSingleInstance() {
return singleInstance;
}
}
即使使用同步循环,您当前的方法实际上已被破坏 - 因为双重检查锁定已被破坏。如果要将其用于双重检查锁定,则需要将单例变量标记为volatile
,否则线程仍有办法访问未完全初始化的对象。有关详细信息,请参阅http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java。
答案 9 :(得分:0)
您可以浏览此链接,有不同的方法来中断和阻止单例类: