にんにんにん

エンジニアな日々を書いていきます

マルチスレッドを考慮したSingleton

例えば、以下のようなJavaコードがあったとします。

public class Singleton {
  private static Singleton singleton = null;

  private Singleton() {
    System.out.println("インスタンスを生成しました");
  }

  public static Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}

このクラスはインスタンスが1個しか生成されなさそうですが、残念ながら複数生成されてしまう場合があります。
それは、マルチスレッド下でgetInstance()を呼んだときです。
つまり、複数のスレッドからほぼ同時にgetInstance()を呼んだとき、状態としてはsingletonはnullであるため、インスタンスは呼び出された分だけ生成されます。

わざとsleep処理をして同時に呼び出されるようにしてみると

public class Singleton {
  private static Singleton singleton = null;

  private Singleton() {
    System.out.println("インスタンスを生成しました");
    slowdawn();
  }

  public static synchronized Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }

  private void slowdawn() {
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

そして、呼び出してみます。

public class Main extends Thread {
  public Main(String name) {
    super(name);
  }

  public static void main(String[] args) {
    System.out.println("Start.");
    new Main("A").start();
    new Main("B").start();
    new Main("C").start();
    System.out.println("End.");
  }

  @Override
  public void run() {
    Singleton obj = Singleton.getInstance();
    System.out.println(getName() + ": obj = " + obj);
  }
}

実行結果はこうなります。

Start.
End.
インスタンスを生成しました
インスタンスを生成しました
インスタンスを生成しました
B: obj = Singleton@12d18a03
A: obj = Singleton@68fb341a
C: obj = Singleton@68fb341a

Process finished with exit code 0

AとCが同じインスタンスなのに対して、Bは違うインスタンスであることがわかります。

そこで、getInstance()にsyncronized をつけてあげることで、ほかのスレッドからブロックします。 こうしてあげることで、スレッドセーフになり、Singletonが成り立ちます。

public class Singleton {
  private static Singleton singleton = null;

  private Singleton() {
    System.out.println("インスタンスを生成しました");
  }

  public static syncronized Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}

実行すると、

Start.
End.
インスタンスを生成しました
A: obj = Singleton@68fb341a
C: obj = Singleton@68fb341a
B: obj = Singleton@68fb341a

Process finished with exit code 0

すべて同じインスタンスとなりました。