JAva同時プログラミングスレッド基礎

15139 ワード

JAva同時プログラミングスレッド基礎
1.javaのマルチスレッド
JAvaは生まれつきのマルチスレッドで、mainメソッドを起動することで、mainメソッドが起動したときにどのくらいのスレッドが同時に起動したかを確認することができます.
public class OnlyMain {
    public static void main(String[] args) {
        //         
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
        for (ThreadInfo info : threadInfos) {
            System.out.printf("[%s] %s
",info.getThreadId(),info.getThreadName()); } } } //////////////////////////////// [6] Monitor Ctrl-Break [5] Attach Listener [4] Signal Dispatcher [3] Finalizer [2] Reference Handler [1] main
  • Monitor Ctrl-Break:IntelliJ IDEAがユーザコードを実行するときは、実際には反射的に呼び出され、同時にモニタ目的のためにMonitor Ctrl-Breakが作成されます.
  • Attach Listener:スレッドは、外部からのコマンドを受信し、コマンドを実行し、結果を送信者に返す役割を果たす.通常、java-version、jmap、jstackなど、jvmにフィードバック情報を要求するコマンドがあります.このスレッドがjvm起動時に初期化されていない場合、ユーザがjvmコマンドを初めて実行したときに起動されます.
  • signal dispather:前述した最初のAttach Listenerスレッドの役割は、外部jvmコマンドを受信することであり、コマンドの受信に成功すると、signal dispatherスレッドに渡して、それぞれの異なるモジュール処理コマンドに配布し、処理結果を返す.signal dispatherスレッドも、外部jvmコマンドを初めて受信したときに初期化作業を行う.
  • Finalizer:JVMはゴミ収集時に参照を失ったオブジェクトをFinalizerオブジェクト(Referenceの実装)に包装し、ReferenceQueueに入れ、Finlizerスレッドで処理する.最後にFinalizerオブジェクトの参照をnullに設定し、ゴミ収集器で回収します.
  • Reference Handler:主に参照オブジェクト自体(ソフト参照、弱参照、ダミー参照)のゴミ回収問題を処理するために使用されます.
  • main:メインスレッドで、私たちが作成したjavaプログラムを実行するためのmainメソッドです.

  • 2.マルチスレッドを起動する方法
  • はThreadクラス
  • を継承する.
  • runnableインタフェース
  • を実装
  • callableインタフェース
    private static class ThreadClass extends Thread{
        @Override
        public void run() {
            System.out.println("this is threadClass");;
        }
    }
    
    private static class RunnableClass implements Runnable{
        @Override
        public void run() {
            System.out.println("this is runnableClass");;
        }
    }
    
    private static class CallableClass implements Callable{
        @Override
        public String call() throws Exception {
            System.out.println("this is callableClass");;
            return "callableClass";
        }
    }
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadClass threadClass = new ThreadClass();
        RunnableClass runnableClass = new RunnableClass();
        CallableClass callableClass = new CallableClass();
        // extends Thread start
        threadClass.start();
        // implements runnable start
        new Thread(runnableClass).start();
        // implements callable start
        FutureTask futureTask = new FutureTask<>(callableClass);
        new Thread(futureTask).start();
        System.out.println(futureTask.get());;
    }
    ////////////////////////////////     
    this is threadClass
    this is runnableClass
    this is callableClass
    callableClass
  • を実現する.
    3.スレッドの停止
  • 自然実行完了または異常放出
  • stop(),resume(),suspend()メソッドを呼び出すが、これらのメソッドスレッドはリソースを解放せず、デッドロックをもたらす.だからjdkに廃棄された
  • interrupt()、isInterrupted()、static interrupted()interrupt()は1つのスレッドを中断し、強制停止ではなく、コラボレーションにより、中断フラグ位置をtrue isInterrupted()として現在のスレッドが中断状態であるか否かを判定するstatic interrupted()は、現在のスレッドが中断状態であるか否かを判定し、割り込みフラグ位置をfalse
    private static class ThreadClass extends Thread {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!isInterrupted()) {
                System.out.println(threadName +" is run!");
            }
            System.out.println(threadName + " flag is " + isInterrupted());
        }
    }
    
    private static class RunnableClass implements Runnable {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println(threadName +" is run!");
            }
            System.out.println(threadName + " flag is " + Thread.currentThread().isInterrupted());
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ThreadClass threadClass = new ThreadClass();
        threadClass.start();
        Thread.sleep(10);
        threadClass.interrupt();
    
        RunnableClass runnableClass = new RunnableClass();
        Thread runnableThread = new Thread(runnableClass,"runnableClass");
        runnableThread.start();
        Thread.sleep(10);
        runnableThread.interrupt();
    }
    warning:スレッドにInterruptedExceptionの異常がある場合、InterruptedExceptionが異常フラグビットをfalseにリセットし、異常中ブレークに影響を与えるため、次のプログラムを何度も繰り返し実行するとこのような異常が発生します.解決策はcatchブロックにinterrupt()を追加することである.
    private static class ThreadClass extends Thread {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!isInterrupted()) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    System.out.println(threadName + " flag1 is " + isInterrupted());
                    //  interrupt();
                    e.printStackTrace();
                }
                System.out.println(threadName + " flag2 is " + isInterrupted());
            }
            System.out.println(threadName + " flag3 is " + isInterrupted());
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ThreadClass threadClass = new ThreadClass();
        threadClass.start();
        Thread.sleep(200);
        threadClass.interrupt();
    }
    
    
    //////////////////////////    
    Thread-0 flag2 is false
    Thread-0 flag2 is false
    Thread-0 flag1 is false
    java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
        at com.thread.demo.InterruptExceptionThread$ThreadClass.run(InterruptExceptionThread.java:16)
    Thread-0 flag2 is false
    Thread-0 flag2 is false
        
    //////////////////////////    
    Thread-0 flag2 is false
    Thread-0 flag2 is false
    Thread-0 flag3 is true
    

  • 4.デーモンスレッド
    現在のJVMインスタンスのいずれかの非デーモンスレッドが終了していない場合、デーモンスレッドはすべて動作します.最後の非デーモンスレッドが終了した場合にのみ、デーモンスレッドはJVMとともに終了します.デーモンスレッドの役割は、他のスレッドの実行に便利なサービスを提供することであり、デーモンスレッドの最も典型的な応用はGC(ゴミ回収器)であり、立派なデーモンである.
    JavaではsetDaemon(true)で1つのスレッドをデーモンスレッドに設定できます
    デーモンスレッドの注意すべき点:
  • thread.setDaemon(true)はthread.start()の前に設定する必要があります.そうしないと、IllegalThreadStateException例外が飛び出します.実行中の通常のスレッドをデーモンスレッドに設定することはできません.
  • Daemonスレッドで生成された新しいスレッドもDaemonのものです.
  • すべてのアプリケーションがDaemonに割り当てられてサービスを行うことができるとは思わないでください.例えば、読み書き操作や計算ロジックなどです.
  • デーモンスレッドにtry...catch...finallyが含まれている場合、finallyの内容が必ずしも実現できるとは限らない.
  • //    
    private static class WriteFileRunnable implements Runnable {
        @Override
        public void run() {
            try {
                File f = new File("daemon.txt");
                FileOutputStream os = new FileOutputStream(f, true);
                os.write("daemon".getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        WriteFileRunnable writeFileRunnable = new WriteFileRunnable();
        Thread writeThread = new Thread(writeFileRunnable);
        writeThread.setDaemon(true);
        writeThread.start();
    }
    //////////////////////////  
    //  daemon.txt   "daemon"   。 
    
    //  try...catch...finally
    private static class ThreadClass extends Thread {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            try {
                while (true) {
                    System.out.println(threadName + " is run!");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("....................finally");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ThreadClass threadClass = new ThreadClass();
        threadClass.setDaemon(true);
        threadClass.start();
        Thread.sleep(10);
    }
    //////////////////////////  
    Thread-0 is run!
    Thread-0 is run!
    Thread-0 is run!
    Thread-0 is run!
    
    Process finished with exit code 0

    5.synchronized内蔵ロック
    同時プログラミングでは、共有データまたはマルチスレッドが共有データを共有しているため、スレッドセキュリティの問題があります.キーワードsynchronizedは、同じ時点でメソッドまたはコードブロックを実行できるスレッドが1つしかないことを保証し、synchronizedはスレッドの変化が可視(可視性)であることを保証します.
    synchronizedは に分けられ、synchronized追加はメソッドにもsynchronized(this)にもオブジェクトロックが使用され、メソッドが静的と定義されている場合はsynchronizedが追加され、このときロックはクラスロックとなる.クラスロックとオブジェクトロックは並列に実行でき、異なるオブジェクトのオブジェクトロックも並列に実行できます.
  • synchronizedがインスタンスメソッド
    public class SynchronizedDemo {
        private static class InstanceSyncMethod implements Runnable {
            static int tag = 1;   //    
    
            public synchronized void increase() {
                tag++;
            }
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    increase();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            InstanceSyncMethod instance = new InstanceSyncMethod();
            Thread t1 = new Thread(instance);
            Thread t2 = new Thread(instance);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println(InstanceSyncMethod.tag);
        }
    }
    //////////////////////////    
    2001
    synchronizedに作用してインスタンスオブジェクトに使用される場合、複数のインスタンスを作成する場合、メソッドではsynchronizedが使用されるが、複数の異なるインスタンスオブジェクトロックが存在するため、tはそれぞれのオブジェクトロック、すなわち複数のスレッドが異なるロックを使用するため、したがって、スレッドのセキュリティは保証されません.
    //new   
    Thread t1=new Thread(new InstanceSyncMethod());
    //new   
    Thread t2=new Thread(new InstanceSyncMethod());
    t1.start();
    t2.start();
    //join  :    A  thread         thread.join()  
    t1.join();
    t2.join();
    System.out.println(InstanceSyncMethod.tag);
    ////////////////////////////                
    このような困難を解決する方法はsynchronizedを静的に作用させる方法であり、そうすると、オブジェクトロックは現在のクラスオブジェクトについて、いくつかのインスタンスオブジェクトを作成しても、クラスオブジェクトに対して1つしかないため、このような場合、オブジェクトロックはすべて一意である.
  • synchronizedは静的方法
  • に作用する.
    synchronizedが静的メソッドに作用する場合、そのロックは現在のクラスのclassオブジェクトロックです.インスタンスオブジェクトを呼び出す非static synchronizedメソッドと静的synchronizedメソッドは、静的synchronizedメソッドにアクセスするロックが現在のクラスのclassオブジェクトであり、静的synchronizedメソッドにアクセスするロックが現在のインスタンスオブジェクトロックであるため、反発現象は発生しません.
    private static class InstanceSyncMethod implements Runnable {
        static int tag = 1;   //    
    
        public static synchronized void increase() {
            tag++;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                increase();
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        //new   
        Thread t1 = new Thread(new InstanceSyncMethod());
        //new   
        Thread t2 = new Thread(new InstanceSyncMethod());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(InstanceSyncMethod.tag);
    }
    //////////////////////////    
    2001
  • synchronized同期コードブロック
  • 場合によっては、メソッドボディが比較的大きい場合と、比較的時間のかかる操作が同時に存在する場合があり、同期が必要なコードはほんの一部であり、直接メソッド全体を同期操作することを推奨しない場合があります.この場合、同期コードブロックを使用して同期が必要な世代コードをラップすることができ、メソッド全体を同期操作する必要がありません.
    private static class InstanceSyncMethod implements Runnable {
        static int tag = 1;   //    
    
        @Override
        public void run() {
            synchronized (this){
                for (int i = 0; i < 1000; i++) {
                    tag++;
                }
            }
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        //new   
        Thread t1 = new Thread(new InstanceSyncMethod());
        //new   
        Thread t2 = new Thread(new InstanceSyncMethod());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(InstanceSyncMethod.tag);
    }
    //////////////////////////    
    2001
        
    
    //////////////////////////      
    //this,       
    synchronized(this){
        ...
    }
    
    //class   
    synchronized(XXX.class){
        ...
    }

    6. volatile
    volatileは変数を使用するたびに、プライマリ・メモリから取得します.各スレッドからの「ワークメモリ」ではありません.
    volatileにはsynchronizedキーワードの「可視性」がありますが、synchronizedキーワードの「同時正確性」はありません.つまり、スレッド実行の秩序性は保証されません.Javaメモリモデルにはmain memoryがあり,各スレッドにも独自のmemoryがある.パフォーマンスのために、スレッドは自分のmemoryでアクセスする変数のコピーを保持します.これにより、ある瞬間、あるスレッドのmemoryの値が別のスレッドのmemoryの値、またはmain memoryの値と一致しない場合があります.
    1つの変数がvolatileとして宣言されると、この変数はいつでも他のスレッドによって変更されるため、スレッドmemoryにcacheすることはできません.
    すなわち、volatile変数は、使用するたびにスレッドが現在のvolatile変数の最新値を得ることができる.しかしvolatile変数は同時の正確性を保証しない.
    次のプログラムはvolatileがスレッドの実行の秩序性を保証できないことを検証します.
    private static class VolatileClass implements Runnable {
        private volatile int a = 1;
    
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            a = a + 1;
            System.out.println(threadName +":=" + a);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a = a + 1;
            System.out.println(threadName +":=" + a);
        }
    }
    
    public static void main(String[] args) {
        VolatileClass volatileClass = new VolatileClass();
    
        Thread t1 = new Thread(volatileClass);
        Thread t2 = new Thread(volatileClass);
        Thread t3 = new Thread(volatileClass);
        Thread t4 = new Thread(volatileClass);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
    //////////////////////////    
    Thread-0:=2
    Thread-3:=5
    Thread-2:=4
    Thread-1:=3
    Thread-3:=7
    Thread-1:=7
    Thread-0:=7
    Thread-2:=7

    7. ThreadLocal
    threadlocalはスレッド内部のストレージクラスであり、指定したスレッド内にデータを格納することができ、データ格納後、指定したスレッドのみが格納データを得ることができる.
    private static ThreadLocal localNum = new ThreadLocal() {
        @Override
        public Integer initialValue() {
            return 1;
        }
    };
    private static class ThreadLoaclClass implements Runnable {
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            localNum.set(localNum.get() + 1);
            System.out.println(threadName +":=" + localNum.get());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            localNum.set(localNum.get() + 1);
            System.out.println(threadName +":=" + localNum.get());
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ThreadLoaclClass loaclClass = new ThreadLoaclClass();
    
        Thread t1 = new Thread(loaclClass);
        Thread t2 = new Thread(loaclClass);
        Thread t3 = new Thread(loaclClass);
        Thread t4 = new Thread(loaclClass);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
    //////////////////////////    
    Thread-0:=2
    Thread-3:=2
    Thread-1:=2
    Thread-2:=2
    Thread-1:=3
    Thread-2:=3
    Thread-0:=3
    Thread-3:=3

    各スレッドによって生成されるシーケンス番号は、同じインスタンスを共有しますが、相互干渉は発生しません.これは、ThreadLocalによって各スレッドに個別のコピーを提供するため、それぞれ独立した数値を生成します.
    ThreadLocalとSynchronizedは、マルチスレッド内の同じ変数のアクセス競合の問題を解決します.
  • Synchronizedは、スレッド待ち、時間を犠牲にしてアクセス競合を解決する
  • です.
  • ThreadLocalは、スレッドごとに個別の記憶領域を持ち、空間を犠牲にして競合を解決し、Synchronizedよりもスレッド分離の効果があり、スレッド内でしか対応する値が得られず、スレッド外では所望の値にアクセスできない.