独習Java第9章 マルチスレッドプログラミング(9.3)

昨日の9.1, 9.2に続いてスレッドの同期の部分。
環境:MacOS 10.6.5 / Oracle(Sun) JDK6 update22

スレッドの同期

スレッドを同期させるには2つ方法がある。

  1. synchronized修飾子を指定してメソッドを同期させる
  2. synchronizedブロックを使う

後者は存在すら知らなかった……
Javaのクラスライブラリのほとんどはスレッドセーフに作られているが、StringBuilderはスレッドセーフではないのでStringBufferを使う必要がある。

書いてみる

// Main.java
class Sync
{
  synchronized void add(int i)
  {
    this._value += i;
  }

  int getValue()
  {
    return this._value;
  }

  private int _value = 0;
}

class ThreadEx extends Thread
{
  ThreadEx(Sync sync)
  {
    this._sync = sync;
  }

  public void run()
  {
    try
    {
      for (int i = 1; i <= 1000000; i++)
      {
        this._sync.add(20);
      }
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  private Sync _sync;
}

class Main
{
  public static void main(String args[])
  {
    Sync sync = new Sync();

    Thread t[] = new ThreadEx[THREAD_COUNT];
    
    for (int i = 0; i < THREAD_COUNT; i++)
    {
      t[i] = new ThreadEx(sync);
      t[i].start();
    }

    for (int i = 0; i < THREAD_COUNT; i++)
    {
      try
      {
        t[i].join();
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }

    System.out.println(sync.getValue());
  }

  private static final int THREAD_COUNT = 100;
}

実行してみる

$ javac Main.java
$ java Main
2000000000

ちゃんと動いた!

書いてみる(その2)

今度はsynchronized修飾子でなく、synchronizedブロックで書いてみる。

4c4
<   void add(int i)
---
>   synchronized void add(int i)
6,9c6
<     synchronized (this)
<     {
<       this._value += i;
<     }
---
>     this._value += i;

実行してみる(その2)

$ javac Main.java
$ java Main
2000000000

synchronizedを書かないで実行してみる

6c6,9
<     this._value += i;
---
>     synchronized (this)
>     {
>       this._value += i;
>     }
$ javac Main.java
$ java Main
1965696700
$ java Main
1956836400
$ java Main
1949922900

前者2つより明らかに速いのだけれど、値が不定に。

おまけ

$ javac -J-Dfile.encoding=UTF-8 Main.java
$ java -Dfile.encoding=UTF-8 Main

で実行するとメッセージが化けないみたい。


今日はここまで。次はデッドロック。できればスレッド間の通信も終わらせたいなー。