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

基于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<Condition> 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<Thread> 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的线程先打印。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

b17a

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值