栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Java实现多线程交替打印a、b、c、......字符(多种实现方式)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java实现多线程交替打印a、b、c、......字符(多种实现方式)

文章目录
    • 基于synchronized+wait/notify
    • 基于ReentrantLock+Condition
    • 基于LockSupport

基于synchronized+wait/notify

思路: 打印线程a、b、c… …依次对应一个标志flag,为了简便就从下标0开始,0、1、2、… …;定义一个全局变量flag,所有打印线程都可以访问,约定只有当全局flag与打印线程的标志一致时才可以打印字符,否则调用wait()方法进入等待;打印完之后,将全局flag设置为下一个要打印字符的线程的标志,并唤醒所有等待的打印线程。

代码实现如下。

public class Main {

    public static final int THREAD_NUM = 3;

    public static void main(String[] args) {
        CyclePrint cyclePrint = new CyclePrint(3, 0);
        for (int i = 0; i < THREAD_NUM; i++) {
            final int curr = i, next = i + 1 < THREAD_NUM ? i + 1 : 0;
            new Thread(() -> cyclePrint.print(curr, next, (char) ('a' + curr) + "")).start();
        }
    }

}

class CyclePrint {

    private int loopNum;

    private int flag;

    public CyclePrint(int loopNum, int flag) {
        this.loopNum = loopNum;
        this.flag = flag;
    }
    
    public void print(int currFlag, int nextFlag, String content) {
        for (int i = 0; i < loopNum; i++) {
            synchronized (this) {
                while (currFlag != this.flag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.print(content);
                this.flag = nextFlag;
                this.notifyAll();
            }
        }
    }
}


运行结果如下,

基于ReentrantLock+Condition

线程1打印a、线程2打印b、线程3打印c、… …,交替打印,即输出的结果为abcabcabc… …
思路: 借助可重入锁ReentrantLock,用它来产生条件变量Condition,约定每个线程对应一个Condition,核心伪代码如下,

loop: {
	lock();
	currCondition.await();
	print();
	nextCondition.signal();
	unlock();
}

最终的实现代码如下,

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    public static final int THREAD_NUM = 3;

    public static void main(String[] args) throws InterruptedException {
        CyclePrint cyclePrint = new CyclePrint(3);
        List conditions = new ArrayList<>(THREAD_NUM);
        for (int i = 0; i < THREAD_NUM; i++) {
            conditions.add(cyclePrint.newCondition());
        }
        // 引用计数器,目的是为了确保第一个线程能顺利进入阻塞状态,再由主线程唤醒
        CountDownLatch cdl = new CountDownLatch(THREAD_NUM);
        for (int i = 0; i < THREAD_NUM; i++) {
            final int curr = i, next = i + 1 < THREAD_NUM ? i + 1 : 0;
            new Thread(() -> {
                cdl.countDown();
                cyclePrint.print(conditions.get(curr), conditions.get(next), (char) ('a' + curr) + "");
            }).start();
        }

        cdl.await();
        // 如果主线程先执行了signal()方法,而打印线程还没来及阻塞,那么后面所有子线程将一直阻塞
        cyclePrint.lock();
        conditions.get(0).signal();
        cyclePrint.unlock();
    }

}

class CyclePrint extends ReentrantLock {

    private int loopNum;

    public CyclePrint(int loopNum) {
        this.loopNum = loopNum;
    }

    public void print(Condition curr, Condition next, String content) {
        for (int i = 0; i < loopNum; i++) {
            this.lock();
            try {
                curr.await();
                System.out.print(content);
                next.signal();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                this.unlock();
            }
        }
    }
}

运行结果为如下,

基于LockSupport

思路跟上面一样,先让所有打印线程阻塞自己,被唤醒后,打印字符,之后再唤醒下一个打印字符的线程,实现如下,

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.LockSupport;

public class Main {

    public static final int THREAD_NUM = 3;

    public static List threads = new ArrayList<>(THREAD_NUM);

    public static void main(String[] args) {
        CyclePrint cyclePrint = new CyclePrint(3);

        for (int i = 0; i < THREAD_NUM; i++) {
            final int curr = i, next = i + 1 < THREAD_NUM ? i + 1 : 0;
            threads.add(new Thread(() -> cyclePrint.print(threads.get(next), (char) ('a' + curr) + "")));
        }

        threads.forEach(thread -> thread.start());

        LockSupport.unpark(threads.get(0));
    }

}

class CyclePrint {

    private int loopNum;

    public CyclePrint(int loopNum) {
        this.loopNum = loopNum;
    }

    public void print(Thread next, String content) {
        for (int i = 0; i < loopNum; i++) {
            LockSupport.park();
            System.out.print(content);
            LockSupport.unpark(next);
        }
    }
}

运行结果一样,

LockSupport这个类比较特殊,原理是调用unpark(Thread thread)方法会给关联thread的计数加一,当thread自己执行park()方法时,会检查它的计数是否大于0,大于0便不会阻塞继续往下运行同时计数减一,否则被阻塞。

也就是说主线程可以先对第一个线程执行unpark(Thread t1),让它计数加一,从而保证了打印字符a的线程先打印。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1012762.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号