SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

マンガで分かるプログラミング用語辞典

「排他制御」
~マンガでプログラミング用語解説

マンガで分かるプログラミング用語辞典(47)

解説

 マルチスレッドのプログラムでは、思わぬ問題に遭遇することがあります。それは例えば、並行して実行している複数のスレッドが、共通のデータを読み込んだり、書き換えたりした際に起きます。

 スレッドAがデータを読み込み、スレッドBがデータを読み込み、スレッドAが変更した値を保存し、スレッドBが変更した値を保存したとします。この場合、スレッドAが変更した内容が、Bによって上書きされてしまいます。

スレッドA   データ   スレッドB
  ↓     □□□     ↓
データ読込←←←□□□     ↓
 □□□    □□□→→→データ読込
  ↓     □□□    □□□
データ書換   □□□     ↓
 □■□    □□□     ↓
  ↓     □□□   データ書換
  ↓     □□□    ■□■
  ↓     □□□     ↓
データ保存→→→□■□     ↓
 □■□    ■□■←←←データ保存
  ↓     ■□■    ■□■
  ↓     ■□■     ↓

 こういった問題を避けるためには、スレッドがデータに対して処理をしている間、他のスレッドがそのデータにアクセスすることに制限を設ける必要があります。そういった制限を設ける方法のことを「排他制御」と呼びます。

 例えば、上記の処理に、制限を与えると、以下のようになります。

スレッドA   データ   スレッドB
  ↓     □□□     ↓
排他制御開始─────┐    ↓
データ読込←←←□□□│    ↓
 □□□    □□□│  排他制御中なので待機
  ↓     □□□│  排他制御中なので待機
データ書換   □□□│  排他制御中なので待機
 □■□    □□□│  排他制御中なので待機
  ↓     □□□│  排他制御中なので待機
データ保存→→→□■□│  排他制御中なので待機
 □■□    □■□│  排他制御中なので待機
排他制御終了─────┘  排他制御中なので待機
  ↓    ┌──────排他制御開始
  ↓    │□■□→→→データ読込
  ↓    │□■□    □■□
  ↓    │□■□     ↓
  ↓    │□■□   データ書換
  ↓    │□■□    ■■■
  ↓    │□■□     ↓
  ↓    │■■■←←←データ保存
  ↓    │■■■    ■■■
  ↓    └──────排他制御終了
  ↓     ■■■     ↓
  ↓     ■■■     ↓

サンプル

 排他制御の仕組みが組み込まれているJavaで、排他制御されたプログラムを書いてみます。

sample/MainClass.java
package sample;

import java.util.Arrays;

public class MainClass {
    static final int SZ = 16;

    public static void main(String[] args) {
        // 同期なし
        System.out.println("同期なし");
        execThread1();
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 同期あり
        System.out.println("\n\n同期あり");
        execThread2();
    }
    
    static void out(String tag, int n0, int n1) {
        System.out.println(tag + " : " + n0 + " ? " + n1 + " = " + (n0 == n1));
    }
    
    static void sleep() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 同期なし
    static void execThread1() {
        final int[] ARR = new int[SZ];
        Arrays.fill(ARR, 100);
        
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < SZ; i ++) {
                    int n = ARR[i] + 10;
                    sleep();
                    ARR[i] = n;
                    out("@thread1@", n, ARR[i]);
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < SZ; i ++) {
                    int n = ARR[i] + 1;
                    sleep();
                    ARR[i] = n;
                    out("#thread2#", n, ARR[i]);
                }
            }
        });
        
        thread1.start();
        thread2.start();
    }

    // 同期あり
    static void execThread2() {
        final int[] ARR = new int[SZ];
        Arrays.fill(ARR, 100);
        final Object sync = new Object();
        
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < SZ; i ++) {
                    synchronized (sync) {
                        int n = ARR[i] + 10;
                        sleep();
                        ARR[i] = n;
                        out("@thread1@", n, ARR[i]);
                    }
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < SZ; i ++) {
                    synchronized (sync) {
                        int n = ARR[i] + 1;
                        sleep();
                        ARR[i] = n;
                        out("#thread2#", n, ARR[i]);
                    }
                }
            }
        });
        
        thread1.start();
        thread2.start();
    }
}
出力結果)
同期なし
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 101 = false
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 101 = false
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 101 = false
#thread2# : 101 ? 101 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true
#thread2# : 101 ? 101 = true
@thread1@ : 110 ? 101 = false
@thread1@ : 110 ? 110 = true
#thread2# : 101 ? 101 = true

同期あり
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
@thread1@ : 110 ? 110 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
#thread2# : 111 ? 111 = true
フィードバックお待ちしております!

 ご感想、解説して欲しい用語、解説内容のアドバイスなどございましたら、FacebookTwitterなどでお気軽に編集部までお寄せください。よろしくお願いいたします。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
マンガで分かるプログラミング用語辞典連載記事一覧

もっと読む

この記事の著者

柳井 政和(ヤナイ マサカズ)

クロノス・クラウン合同会社 代表社員http://crocro.com/オンラインソフトを多数公開。プログラムを書いたり、ゲームを作ったり、記事を執筆したり、マンガを描いたり、小説を書いたりしています。「めもりーくりーなー」でオンラインソフト大賞に入賞。最近は、小説家デビューして小説も書いています(『裏切りのプログラム』他)。面白いことなら何でもOKのさすらいの企画屋です。 

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7564 2014/02/11 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー