Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.

In the previous article, we talked about how to implement the singleton pattern, so today we’ll look at how to break it and, in turn, how to fix it.

Antecedents to review

There are three axes to realize singleton mode. Privatized constructor 2 creates a private instance object. 3 Provides a common method to obtain instance

@threadSafe Public class SingletonExample2 {// Private constructor private SingletonExample2(){} // provide an instance private static SingletonExample2 instance = new SingletonExample2(); Public static SingletonExample2 getInstance(){return instance; }}Copy the code

We call this thread-safe, but it’s thread-safe, it’s not really safe, and if you really want to break it, it’s possible.

Use reflection to create objects

Although we have privatized the constructor, we can still create objects through reflection, which is the vulnerability of this singleton.

public static void main(String[] args) throws Throwable { SingletonExample2 instance = SingletonExample2.getInstance(); Class<? > clazz = Class.forName("com.kris.workingtimes.practice.SingletonExample2"); Constructor<? > constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); SingletonExample2 instance1 = (SingletonExample2) constructor.newInstance(); if(instance == instance1) { System.out.println("Singleton break failed!" ); }else { System.out.println("Singleton break succeed!" ); }}Copy the code

We privatize the constructor to ensure that the class can only be created by itself, but we also make the singleton unsafe by reflection. Now that you know the problem, how about solving it?

We just need control in the constructor to ensure that the constructor is called only once, and that if someone calls the constructor again when they already have an instance, an error is reported.

Private SingletonExample2(){if(instance! = null) { throw new RuntimeException("No reflect please ..." ); }}Copy the code

Create objects using deserialization

In order to achieve this kind of destruction, one of the conditions is that your singleton already implements the sequential communication interface…

Break the code like this.

public static void main(String[] args) throws Exception { SingletonExample2 instance1 = SingletonExample2.getInstance();  ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("tempFile")); objectOutputStream.writeObject(instance1); File file = new File("tempFile"); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file)); SingletonExample2 instance2 = (SingletonExample2) objectInputStream.readObject(); if (instance1 == instance2) { System.out.println("Singleton break failed!" ); } else { System.out.println("Singleton break succeed!" ); }}Copy the code

Well, now that we know the premise of this break, it is also easy to solve, directly do not implement serialization interface. You’re kidding. It could be, but what if you need to transfer this object over the network?

Another option is to override the readResolve method, which is called underneath the readObject method of the calling object, so we simply return our defined object in readResolve.

    private Object readResolve() {
        return instance;
    }
Copy the code

Breaking singletons with Unsafe objects

This last one is awesome, because I don’t know how to prevent it, and if you do, you’re welcome to discuss it.

    public static void main(String[] args) throws Exception {
        Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeField.setAccessible(true);
        Unsafe unsafeInstance = (Unsafe)theUnsafeField.get(null);
        SingletonExample2 instanceA = (SingletonExample2)unsafeInstance.allocateInstance(SingletonExample2.class);
        SingletonExample2 instanceB = (SingletonExample2)unsafeInstance.allocateInstance(SingletonExample2.class);
        System.out.println(instanceA.hashCode());
        System.out.println(instanceB.hashCode());
        System.out.println(instanceA == instanceB);
    }
Copy the code

As you can see, Unsafe doesn’t call the constructor at all. In fact, Unsafe doesn’t need to call the constructor. Unsafe uses C++ for low-level JVM control, so how does that work?

conclusion

So those are the three patterns that break singletons, but but, I mean, these are the things that you implement in your project, you don’t, you write singletons and you write a break singletons, good for you. And as far as I know, the singleton pattern is not used much in our daily development business systems, but more in some frameworks.