1
1

More than 1 year has passed since last update.

競合対策方法 【初心者向け】

Posted at

競合とは

一言で言うと、複数のスレッドで一つのインスタンスを共有すると競合が起きます。こうなることで、インスタンスのフィールドの値を読みだしてから、変更するまでの間に 、別のスレッドが変更してしまいます。いつ実行されるかはプログラミング側で制御することが出来ません。飲食店に例えると、あるお客さんがカレーを注文したが、料理人がカレーを作っている間にカレーからオムライスに変更されてしまったというイメージです。

スクリーンショット 2022-02-23 9.54.42.png

対策

結論から言うと、対策はスレッドから処理している間は別のスレッドで処理が実行されないようにすることです。これを排他制御と呼びます。排他制御の方法を紹介していきます。

方法

synchronizedキーワードを使用します。パターンは2つあります。
①メソッド宣言に使う場合
②メソッド内の1部の処理だけを対象に使う場合

メソッド宣言に使う場合

synchronizedを修飾するだけです。
しかし、メソッド内の処理が複雑であればあるほど、待ち時間が出来てしまいます。

public synchronized void method(){
   doSomething;
}

メソッド内の一部の処理だけを対象に使う場合

syncronizedブロックを使用するだけです。どのインスタンスに対して、排他制御するのかをブロック引数として指定します。一部の制御をすることで、待ち時間を最小限にすることが出来ます。

void method(){
  syncronized(instance){
     doSomething()
 }

問題点

複数のスレッド間で排他制御された複数のインスタンスを共有していて、それぞれのインスタンス同士が連携することで、デットロックが発生してしましいます。解決方法として、ロックする順番をスレッド間で揃える方法があります。また、syncnizedは別のスレッドを待機させるので、パフォーマンスに影響を及ぼしてしまいます。その対策として、単純な処理には実行されないか、一連の処理が完全に終わるかになるような結果が保証されているjava.util.concurrent.atomicパッケージを利用します。

java.util.concurrent.atomicパッケージのAtomicIntegerクラスの使用例

atomicパッケージのAtomicIntegerクラスを使用して、排他制御していきます。
atomicパッケージにどのようなクラスがあるのかは、下記のサイトを参考にしてください。

Value.java
public class Value {
    private int num = 0;

    public void add(int num) {
        this.num +=num;
    }

    public int get() {
        return num;
    }
}

AtomicIntegerクラスのaddAndGedメソッドを使うことで、値の呼び出しから、変更までの間に他のスレッドは処理できない。

AtomicValue.java
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicValue extends Value{
    private AtomicInteger num = new AtomicInteger(0);
    public void add(int num) {
        //読み出しから値の変更まで他のスレッドからの処理は受けない
         this.num.addAndGet(num);
    }
     public int get() {
         return this.num.intValue();
     }
}
Sample.java
public class Sample {
    public static void main(String[] args) {
        Value val = new AtomicValue();
        //スレッドプールを2つ作成
        ExecutorService exec = Executors.newFixedThreadPool(2);
        exec.submit(new Task(val));
        exec.submit(new Task(val));

        try {
            Thread.sleep(200);
        }catch(InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(val.get());
        exec.shutdown();
    }
}

200

まとめ

競合を防ぐにはsyncronizedキーワードを使用する
デットロックを防ぐにはロックする順番をスレッド間で揃える

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1