Java同時実行:2つのスレッドが交互に2つの配列の要素を印刷する

47354 ワード

2文字配列
  • number = {‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’}
  • letter = {‘A’,‘B’,‘C’,‘D’,‘E’,‘F’,‘G’,‘H’,‘I’}

  • 2つのスレッドを起動し、要素を交互に印刷し、結果を出力する必要があります.
  • 1 A 2 B 3 C 4 D 5 E 6 F 7 G 8 H 9 I

  • 本論文では,クラスLockSupport,スピンロック,wait/notify,ReentrantLockがこの機能を実現する方法を示した.また、Semaphore(信号量を用いてストリーム制限を実現し、acquireとrelease方式で毎回1つのスレッドのみ実行を許可する)、BlockingQueue(2つのBlockingQueueを使用する必要があり、2つのBlockingQueue間の情報相互作用とput、take方法で成功エネルギーを達成する必要がある)、PipedStream(PipedStream)などの方法もあります.
    LockSupportの方法
    最も簡単な方法は、parkとunparkを使用して2つのスレッド間の通信を完了することです.
    public static void main(String[] args) {
            char[] number = {'1','2','3','4','5','6','7','8','9'};
            char[] letter = {'A','B','C','D','E','F','G','H','I'};
             t1 = new Thread(() ->{
                for(char num : number){
                    System.out.print(num + " ");
                    LockSupport.unpark(t2);  //  t2  
                    LockSupport.park();  //      
                }
            });
    
             t2 = new Thread(() ->{
                for(char let : letter){
                    LockSupport.park();  //  t2          A
                    System.out.print(let + " ");
                    LockSupport.unpark(t1);  //  t1  
                }
            });
    
            t1.start();
            t2.start();
    }
    

    アナログスピンロック方式
    原子型の値をスピンの表示として使用し、スレッドが実行するときに表示が間違っていることに気づいたらwhileスピン待機します.
    static final AtomicInteger threadNo = new AtomicInteger(1);
    
    public static void main(String[] args) {
            char[] number = {'1','2','3','4','5','6','7','8','9'};
            char[] letter = {'A','B','C','D','E','F','G','H','I'};
    
            new Thread(() ->{
                for(char num : number){
                    while(threadNo.get() != 1){}
                    System.out.print(num + " ");
                    threadNo.set(2);
                }
            }).start();
    
            new Thread(() -> {
                for(char let : letter){
                    while(threadNo.get() != 2){}
                    System.out.print(let + " ");
                    threadNo.set(1);
                }
            }).start();
    }
    

    wait/notify方式
    最も古典的な方法では、wait/notifyを使用してスレッド通信を完了します.ここでsynchronizedはthisを使用できません.これは静的方法では、カスタムモニタのほかにクラスのClassオブジェクトも使用できます.以下の書き方では、1を先に出力するかAを正しく保証できません.読者は自分でCountDownLatchを追加して指定された順序で出力する機能を完了することができます.
    public static void main(String[] args) {
            final Object obj = new Object();
            char[] number = {'1','2','3','4','5','6','7','8','9'};
            char[] letter = {'A','B','C','D','E','F','G','H','I'};
    
            new Thread(() -> {
                synchronized (obj){
                    for(char num : number){
                        System.out.print(num + " ");
                        try {
                            obj.notify(); //      ,    t2
                            obj.wait(); //     ,   
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    obj.notify(); //    ,       try          ,                ,     ,        
                }
            }).start();
    
            new Thread(() -> {
                synchronized (obj){
                    for(char let : letter){
                        System.out.print(let + " ");
                        try {
                            obj.notify(); //      ,   t1
                            obj.wait(); //     ,   
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    obj.notify(); //  
                }
            }).start();
    }
    

    ReentrantLock-単一condition方式
    この方式はwait/notify方式と同様であり,ReentrantLockを用いて異なるスレッドに対してconditionをカスタマイズできる利点は現れない.
    public static void main(String[] args) {
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
    
            char[] number = {'1','2','3','4','5','6','7','8','9'};
            char[] letter = {'A','B','C','D','E','F','G','H','I'};
    
            new Thread(() -> {
                lock.lock();
                try{
                    for(char num : number){
                        System.out.print(num + " ");
                        condition.signal();
                        condition.await();
                    }
                    condition.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }).start();
    
            new Thread(() -> {
                lock.lock();
                try{
                    for(char let : letter){
                        System.out.print(let + " ");
                        condition.signal();
                        condition.await();
                    }
                    condition.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }).start();
    }
    

    ReentrantLock-マルチコントロール方式
    2つのconditionを使用して、異なるスレッドの監視を実現し、ReentrantLockのwait/notifyに対する優位性を体現します.
    public static void main(String[] args) {
            Lock lock = new ReentrantLock();
            Condition conditionNum = lock.newCondition();
            Condition conditionLet = lock.newCondition();
    
            char[] number = {'1','2','3','4','5','6','7','8','9'};
            char[] letter = {'A','B','C','D','E','F','G','H','I'};
    
            new Thread(() -> {
                lock.lock();
                try{
                    for(char num : number){
                        System.out.print(num + " ");
                        conditionLet.signal();
                        conditionNum.await();
                    }
                    conditionLet.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }).start();
    
            new Thread(() -> {
                lock.lock();
                try{
                    for(char let : letter){
                        System.out.print(let + " ");
                        conditionNum.signal();
                        conditionLet.await();
                    }
                    conditionNum.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally{
                    lock.unlock();
                }
            }).start();
    }
    

    3つのスレッドを起動し、各スレッドに1つの数字を出力し、1,2,3を順番に出力する必要があります.
    上記で最も簡単なLockSupport方式で、スレッドを1つずつ起動して実行することができます.
    //  :  t2 t3    park(  ) ,        1,          
    static  Thread t11 = null, t22 = null, t33 = null;
    public static void main(String[] args) {
            t11 = new Thread(() -> {
                System.out.println(1);
                LockSupport.unpark(t22);
            });
    
            t22 = new Thread(() -> {
                LockSupport.park();
                System.out.println(2);
                LockSupport.unpark(t33);
            });
    
            t33 = new Thread(() -> {
                LockSupport.park();
                System.out.println(3);
            });
    
            t11.start();
            t22.start();
            t33.start();
    }