Post

Singleton

๐Ÿ› ๏ธ When to use singleton

  • when only one instance should exist
  • ๐Ÿ‘€ settings of an application

โœ… How to create singleton

  • should not use new โŒ
  • create method getInstance()

  • โœ”๏ธ Setting instance class
1
2
3
4
5
6
7
8
9
10
11
12
public class Settings {
    private  Settings() {}
    //private constructor
    private static Settings instance;
    public static Settings getInstance(){
        if(instance == null){
            return new Settings();
        }else{
            return instance;
        }
    }
}
  • โœ”๏ธ Main class
1
2
3
4
5
6
7
8
9
public class App {
    public static void main(String[] args) {
        //In singleton, cannot create instance with constructor โŒ
        //Settings settings = new Settings();

        //how to create instance in singleton โžก๏ธ use getInstance()
        Settings settings = Settings.getInstance();
    }
}

โœ… How to create singleton in multithreading environment

  • how to create singleton thread-safe

1๏ธโƒฃ Use synchronized

  • ๐Ÿ‘๐Ÿป threadsafe
  • ๐Ÿ‘Ž๐Ÿป lots of resources in using synchronized
  • ๐Ÿ‘Ž๐Ÿป aquiring, releasing lock is heavy work
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Settings {
    private  Settings() {}
    //private constructor
    private static Settings instance;

    // use synchronized
    public static synchronized Settings getInstance(){
        if(instance == null){
            return new Settings();
        }else{
            return instance;
        }
    }
}

2๏ธโƒฃ Eager initialization

  • create instance when the class is loaded, not when itโ€™s first requested

  • ๐Ÿ‘๐Ÿป threadsafe, instance is created once
  • ๐Ÿ‘๐Ÿป create instance before, if creating the instance is not so heavy
  • ๐Ÿ‘Ž๐Ÿป instance is created even if never used
  • ๐Ÿ‘Ž๐Ÿป no lazy loading
1
2
3
4
5
6
7
8
9
10
11
12
public class Settings {
    //initiate setting instance
    //use static final
    private static final Settings INSTANCE = new Settings();

    //private constructor
    private  Settings() {}

    public static  Settings getInstance(){
            return INSTANCE;
    }
}

3๏ธโƒฃ Double checked locking

  • check if instance is null two times
  • ๐Ÿ‘๐Ÿป do not use synchronized every time getInstance() is called
  • ๐Ÿ‘๐Ÿป the instance is created when it is needed, lazy loading
  • (no lazy loading was disadvantage of eager initialization)
  • ๐Ÿ‘Ž๐Ÿป keyword volatile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Settings {

    private static volatile Settings instance;
    private  Settings() {}

    public static Settings getInstance(){
            if(instance == null ){ //check 1
                synchronized (Settings.class){
                    if(instance == null){ //check2
                        instance = new Settings();
                    }
                }
            }
            return instance;
    }
}

4๏ธโƒฃ Double checked locking without volatile

1
2
3
4
5
6
7
8
9
10
11
12
public class Settings {

    private  Settings() {}

    //create an inner class
    private static class SettingsHolder{
        private static final Settings INSTANCE = new Settings();
    }
    public static  Settings getInstance(){
            return SettingsHolder.INSTANCE;
    }
}

๐Ÿ’” How can we break singleton?

  • ์•„๋ฌด๋ฆฌ ๋‚ด๊ฐ€ ์ด์˜๊ฒŒ singleton์„ ๊ตฌํ˜„ํ•ด๋‘๋”๋ผ๋„
  • singleton ํด๋ž˜์Šค ๋ฐ–์—์„œ reflection์„ ์“ฐ๊ฑฐ๋‚˜ Serialization์„ ํ•ด๋ฒ„๋ฆฌ๋ฉด
  • ๋‚ด๊ฐ€ ์—ด์‹ฌํžˆ ๋งŒ๋“  singleton์ด ๊นจ์ง€๊ฒŒ๋œ๋‹ค

  • Serialization์€ ๋Œ€์‘ ๋ฐฉ๋ฒ•์ด๋ผ๋„ ์žˆ์ง€, reflection์€ ๋Œ€์‘ ๋ฐฉ๋ฒ•๋„ ์—†์Œ

1๏ธโƒฃ By using reflection

  • create the constructor from outside Settings class
  • inside main method()
  • no way to solve reflection problem
1
2
3
4
5
6
7
8
9
10
11
public class App {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Settings settings = Settings.getInstance();

        Constructor<Settings> constructor = Settings.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Settings settings1 = constructor.newInstance();

        System.out.println(settings ==  settings1);
    }
}
  • This code accesses the private constructor via Reflection
  • gets the private constructor of Settings ๐Ÿ˜ฑ
  • and breaks access control!!!!!
  • and creates a second instance

2๏ธโƒฃ Serialization

  • serialization: save java into a file
  • deserialization: start with byte stream and recreate the saved file into java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class App {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Settings settings = Settings.getInstance();
        Settings settings1 = null;

        try(ObjectOutput out = new ObjectOutputStream(new FileOutputStream("settings.obj"))){
            out.writeObject(settings);
        }

        try(ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))){
           settings1 = (Settings) in.readObject();
        }
    }
}
//now settings and settings1 will be different

๐Ÿ’Š How to save singleton in serialization issue

  • in deserialization, it uses the readResolve() method
  • so, Override the readResolve() method
  • and make it return getInstance()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Settings implements Serializable {

    private  Settings() {}

    private static class SettingsHolder{
        private static final Settings INSTANCE = new Settings();
    }
    public static  Settings getInstance(){
            return SettingsHolder.INSTANCE;
    }

    //add readResolve()
    public Object readResolve(){
        return getInstance();
    }
}

โœ… How to implement singleton in safe, simple manner

  • make singleton class ENUM

  • ENUM settings

1
2
3
public enum Setting {
    INSTANCE
}
  • As ENUM does not allow creating constructor from outside, reflection problem solved
  • Now in main class, serialization problem solved
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class App {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Setting setting = Setting.INSTANCE;
        Setting setting1 = null;
        try(ObjectOutput out = new ObjectOutputStream(new FileOutputStream("settings.obj"))){
            out.writeObject(setting);
        }

        try(ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))){
            setting1 = (Setting) in.readObject();
        }

        System.out.println(setting == setting1); //true
    }
}
  • ๐Ÿ‘Ž๐Ÿป class has to be created before being used
  • ๐Ÿ‘Ž๐Ÿป no inheritence can be used in ENUM

๐Ÿ› ๏ธ When is singleton used?

1๏ธโƒฃ java.lang.Runtime

image.png

  • can only create one instance of Runtime
  • need to create instance with getRuntime()
  • ๐Ÿ› ๏ธ used to measure java resources in runtime
1
2
3
4
5
6
7
public class RuntimeExample {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime.maxMemory());

    }
}

2๏ธโƒฃ Singleton scope

  • strictly speaking not singleton,
  • but to create bean scope
1
2
3
4
5
6
7
8
9
10
public class SpringExample {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        String hello = applicationContext.getBean("hello", String.class);
        String hello2 = applicationContext.getBean("hello", String.class);

        System.out.println(hello == hello2); //true
    }
}

This post is licensed under CC BY 4.0 by the author.