JUC-Condition和Lock实践-线程按序交替执行

Posted 美好的明天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC-Condition和Lock实践-线程按序交替执行相关的知识,希望对你有一定的参考价值。

编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
如:ABCABCABC…… 依次递归

这里只使用conditon和Lock组合使用,不考虑synchronized和wait的方式:

 

第一种方式:使用一个condition,跟while条件组合。

通过signalAll,每次执行完唤醒所有的线程。

每个线程唤醒后,是否阻塞通过while里面的变量值来决定:

package com.atguigu.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
 *    如:ABCABCABC…… 依次递归
 */
public class TestABCAlternate {
    
    public static void main(String[] args) {
        AlternateDemo ad = new AlternateDemo();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                //注意:各个线程的循环次数必须相等,否则当一个线程唤醒另一个Condition的时候,对应
                //唤醒锁所在的线程已经执行完了,此时就会一直等待,而其他没执行完的线程则一直阻塞。
                for (int i = 1; i <= 20; i++) {
                    ad.loopA(i);
                }
                
            }
        }, "A").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                for (int i = 1; i <= 20; i++) {
                    ad.loopB(i);
                }
                
            }
        }, "B").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                for (int i = 1; i <= 20; i++) {
                    ad.loopC(i);
                    
                    System.out.println("-----------------------------------");
                }
            }
        }, "C").start();
    }
}

class AlternateDemo{
    
    private int number = 1; //当前正在执行线程的标记
    
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition(); 
    /**
     * @param totalLoop : 循环第几轮
     */
    public void loopA(int totalLoop){
        lock.lock();
        
        try {
            //1. 判断,如果number不为1,则该线程阻塞
            while(number != 1){
                condition.await();
            }
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }
            
            //3. 修改值来决定哪个线程能够下一个执行,并且唤醒所有的线程。
            number = 2;
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void loopB(int totalLoop){
        lock.lock();
        try {
            //1. 判断
            while(number != 2){
                condition.await();
            }
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }            
            //3. 唤醒
            number = 3;
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void loopC(int totalLoop){
        lock.lock();
        
        try {
            //1. 判断
            while(number != 3){
                condition.await();
            }
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }           
            //3. 唤醒
            number = 1;
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }   
}

运行结果:

 

 

第二种方式:每个线程分配一个condition对象,与if组合。

1,通过conditionA.signal()来唤醒conditionA所wait的线程,这种方式可以指定唤醒哪个线程,以此来实现线程间通信。

所以不会出现唤醒其他错误的线程,而需要通过while循环判断的情况,只需要if即可。

2,定义一个变量的值,通过改变这个变量的值,来决定究竟当前线程是否进入等待。

 

例如,这里有三个线程,则需要三个condition对象,每个condition对象分别分配到不同的线程运算里面,进行await,signal等操作,

通过不同condition对象之间的相互通信,互相唤醒。

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

实现代码:

package com.atguigu.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 编写一个程序,开启 3 个线程,这三个线程的 ID 分别为 A、B、C,每个线程将自己的 ID 在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。
 *    如:ABCABCABC…… 依次递归
 */
public class TestABCAlternate {
    
    public static void main(String[] args) {
        AlternateDemo ad = new AlternateDemo();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                //注意:各个线程的循环次数必须相等,否则当一个线程唤醒另一个Condition的时候,对应
                //唤醒锁所在的线程已经执行完了,此时就会一直等待,而其他没执行完的线程则一直阻塞。
                for (int i = 1; i <= 20; i++) {
                    ad.loopA(i);
                }
                
            }
        }, "A").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                for (int i = 1; i <= 20; i++) {
                    ad.loopB(i);
                }
                
            }
        }, "B").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                
                for (int i = 1; i <= 20; i++) {
                    ad.loopC(i);
                    
                    System.out.println("-----------------------------------");
                }
                
            }
        }, "C").start();
    }

}

class AlternateDemo{
    
    private int number = 1; //当前正在执行线程的标记
    
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    
    /**
     * @param totalLoop : 循环第几轮
     */
    public void loopA(int totalLoop){
        lock.lock();
        
        try {
            //1. 判断
            if(number != 1){
                condition1.await();
            }
            
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }
            
            //3. 修改number值,唤醒condition2的等待线程,使得condition2.await()后面的代码继续执行
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void loopB(int totalLoop){
        lock.lock();
        
        try {
            //1. 判断
            if(number != 2){
                condition2.await();
            }
            
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }
            
            //3. 唤醒
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void loopC(int totalLoop){
        lock.lock();
        
        try {
            //1. 判断
            if(number != 3){
                condition3.await();
            }
            
            //2. 打印
            for (int i = 1; i <= 1; i++) {
                System.out.println(Thread.currentThread().getName() + "\\t" + i + "\\t" + totalLoop);
            }
            
            //3. 唤醒
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

 执行结果:

 

以上是关于JUC-Condition和Lock实践-线程按序交替执行的主要内容,如果未能解决你的问题,请参考以下文章

java 面试题 -- 线程 按序 交替

面时莫慌 | Reetrant[ReadWrite]Lock类图分析实践

Java多线程:synchronized关键字和Lock

面试官:你说说如何让“线程”按序执行?

LeetCode——多线程问题汇总

多线程按序交替打印