javaの同時プログラミングの1つのAtomicBoolean紹介を笑います。


Javaのjava.util.co ncurrent.atomicというパッケージの下には多くの原子的操作のapiが提供されています。マルチスレッドで動作する原子性を保証できます。スレッドが安全でない操作は起こりません。
  • により、ある時間帯に作業員が一人しかいない例を実現しました。スレッドのセキュリティコードは以下の通りです。
    public class BarWorker implements Runnable {
        //                    ,            
         private static boolean exists = false;    
         private String name;
         public BarWorker(String name) {
              this.name = name;    
         }    
         @Override
         public void run() {     
             if (!exists) {
                 try {
                     TimeUnit.SECONDS.sleep(1);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 exists = true;
                 System.out.println(name + " enter");
                     try {    
                      System.out.println(name + " working");    
                      TimeUnit.SECONDS.sleep(2);    
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println(name + " leave");    
                     exists = false;    
            } else {    
             System.out.println(name + " give up");    
            }    
        }
         public static void main(String[] args) throws InterruptedException {
             BarWorker bar1 = new BarWorker("bar1");
             BarWorker bar2 = new BarWorker("bar2");  
             new Thread(bar1).start();
             new Thread(bar2).start();
        }
    }
    出力結果は以下の通りです。
    bar1 enter
    bar2 enter
    bar1 working
    bar2 working
    bar2 leave
    bar1 leave
    結論:同時に2人の従業員が作業に入ったのを見ることができます。これはexistsが判断と更新の間に原子的な操作ではないためです。途中でスレッドを使って1秒待ちました。第2労働者が入ってから、existsがまだfalseであることを発見しました。主な問題は判断と更新の間に他の仕事が発生しました。もちろん、判断と更新の間の待ち時間を取り除ければ、基本的に上記の問題は発生しません。しかし、完全に保証できません。この場合、同期synchronizedを採用します。でも本当にそうしなければなりませんか?実はそうではないです。聡明なjava大師たちは原子的操作のAtomicBooleanツールを提供してくれました。
  • AtomicBoolean上記問題を解決するコード案:
  • import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class BarWorkerAuto implements Runnable {
        //                    ,            
         private static AtomicBoolean exists = new AtomicBoolean(false);
         private String name;
         public BarWorkerAuto(String name) {
              this.name = name;    
         }    
         @Override
         public void run() {     
             if (exists.compareAndSet(false,true)) {
                 System.out.println(name + " enter");
                     try {    
                      System.out.println(name + " working");    
                      TimeUnit.SECONDS.sleep(2);    
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println(name + " leave");
                 exists.set(false);
            } else {    
             System.out.println(name + " give up");    
            }    
        }
         public static void main(String[] args) throws InterruptedException {
             BarWorkerAuto bar1 = new BarWorkerAuto("bar1");
             BarWorkerAuto bar2 = new BarWorkerAuto("bar2");
             new Thread(bar1).start();
             new Thread(bar2).start();
        }
    }
    出力結果は以下の通りです。
    bar1 enter
    bar2 give up
    bar1 working
    bar1 leave
    
    結論:同じ時間に一人の労働者だけが仕事に入ることが見られます。AtomicBooleanのcompreAndSetという方法は原子的な操作です。まず現在の値がexpectの値と同じかどうかを判断します。等しいなら、値をアップデートします。
    public final boolean compareAndSet(boolean expect, boolean update) {
            int e = expect ? 1 : 0;
            int u = update ? 1 : 0;
            return unsafe.compareAndSwapInt(this, valueOffset, e, u);
        }