<< Layout switching | Home | 大先輩がdisられているようなので、一言、言っておくか。 >>
PR: 転職    お墓    エコ    通販    結婚相談所    シルバー    質屋    葬式    漫画    エステサロン   

匿名クラスとScalaのクロージャ

Javaでは、匿名クラスから、ローカル変数にアクセスする際には、final宣言することが必須となる。

public class Test {
    public static void main(String[] args) throws InterruptedException {
        int sum = 0;
        Thread t = new Thread() {
            @Override public void run() {
                sum = sum + 1;
            }
        };
        t.start();
        t.join();
        System.out.println("sum = " + sum);
    }
}

これは、コンパイルできない。sumがfinal宣言されていないからだ。これはJavaのメモリモデルの仕様から来ている。ローカル変数はスレッドのプライベートなメモリ空間に置かれるので、別のスレッドからは直接アクセスできない。このため、匿名クラスからのアクセスにあたっては、匿名クラスのオブジェクトに値がコピーされる。コピー後に値が変更されても、もちろん反映されない。それじゃわけが分からなくなるから、finalを必須にしようというわけだ。でも、もちろんfinal付けたら、今度は、sum = sum + 1なんてことはできなくなる。でも、これはScalaでは出来るんだよね。

import System.out

object Test {
  def main(args: Array[String]) {
    @volatile var sum = 0
    def bar() {
      sum = sum + 1
    }
    val t = new Thread() {override def run() {sum = sum + 1}}
    t.start()
    t.join()
    System.out.println("sum = " + sum)
  }
}

なんで? と思ったら、この場合は、IntRefというクラスのオブジェクトがsumの値を保持するようなコードが吐かれる。なので、@volatileも必要だ。ローカル変数に@volatileなわけですよ。Javaでも苦しまぎれに、こんなことならできる。

public class Test {
    public static void main(String[] args) throws InterruptedException {
        final int[] sum = {0};
        Thread t = new Thread() {
            @Override public void run() {
                sum[0] = sum[0] + 1;
            }
        };
        t.start();
        t.join();
        System.out.println("sum = " + sum[0]);
    }
}

でも、これってスレッド安全性の観点からは、同期化されていないからマズイよね。しかしながらJavaの場合はローカル変数にvolatileは付けられないから、マルチスレッドで使用するなら、synchronizedで囲まないといけないことになるな。




コメント追加 トラックバック送信
このサイトの掲載内容は私自身の見解であり、必ずしもIBMの立場、戦略、意見を代表するものではありません。
日本アイ・ビー・エム 花井 志生 Since 1997.6.8