多线程:循环打印ABC

前言:这是一道百度的面试题,查阅资料学习后发现方法还挺多的,这里给出三种方法,分别是:

  1. 两个锁
  2. ReenTrantLock加condition来管理
  3. 一个锁+一个状态位

两个锁

思想:三个线程,每一个线程都有一把锁,当一个线程同时拿到两把锁才能去打印字符,代码如下:

package ConcurrentTest;

public class XuTwoLockPrinter implements Runnable{

    // 前一个锁
    Object frontLock;

    // 自己的锁
    Object ownLock;

    char printChar;

    XuTwoLockPrinter (Object frontLock,Object ownLock,char printChar) {
        this.frontLock = frontLock;
        this.ownLock = ownLock;
        this.printChar = printChar;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (frontLock) {
                synchronized (ownLock) {
                    System.out.print(printChar);
                    // 唤醒线程
                    ownLock.notify();
                }

                if (i < 9) {
                    try {
                    //释放锁,等待被唤醒
                        frontLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Object lockA = new Object();
        Object lockB = new Object();
        Object lockC = new Object();

        Thread printerA = new Thread(new XuTwoLockPrinter(lockC,lockA,'A'));
        Thread printerB = new Thread(new XuTwoLockPrinter(lockA,lockB,'B'));
        Thread printerC = new Thread(new XuTwoLockPrinter(lockB,lockC,'C'));

        printerA.start();
        Thread.sleep(100);
        printerB.start();

        printerC.start();
    }
}

注:这里最后一次要做一次判断,不然会引起死锁。

ReenTrantLock加condition来管理

思想:与上面的方法想法类似,代码如下:

package ConcurrentTest;

import org.junit.Test;

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

public class RcSyncPrinter {

    @Test
    public void test() throws InterruptedException {
        ThreadGroup group = new ThreadGroup("xx");
        // 写锁
        ReentrantLock lock = new ReentrantLock();
        // 打印a线程的condition
        Condition conditionA = lock.newCondition();
        // 打印b线程的condition
        Condition conditionB = lock.newCondition();
        // 打印c线程的condition
        Condition conditionC = lock.newCondition();
        // 实例化A线程
        Thread printerA = new Thread(group, new Printer(lock, conditionA, conditionB, 'A'));
        // 实例化B线程
        Thread printerB = new Thread(group, new Printer(lock, conditionB, conditionC, 'B'));
        // 实例化C线程
        Thread printerC = new Thread(group, new Printer(lock, conditionC, conditionA, 'C'));
        // 依次开始A B C线程
        printerA.start();
        Thread.sleep(100);
        printerB.start();
        Thread.sleep(100);
        printerC.start();
        // 主线程循环让出CPU使用权
        while (group.activeCount() > 0) {
            Thread.yield();
        }
    }

    // 打印线程
    private class Printer implements Runnable {
        // 打印次数
        private static final int PRINT_COUNT = 6;
        // 打印锁
        private final ReentrantLock reentrantLock;
        // 本线程打印所需的condition
        private final Condition thisCondtion;
        // 下一个线程打印所需要的condition
        private final Condition nextCondtion;
        // 打印字符
        private final char printChar;

        public Printer(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, char printChar) {
            this.reentrantLock = reentrantLock;
            this.nextCondtion = nextCondition;
            this.thisCondtion = thisCondtion;
            this.printChar = printChar;
        }

        @Override
        public void run() {
            // 获取打印锁 进入临界区
            reentrantLock.lock();
            try {
                // 连续打印PRINT_COUNT次
                for (int i = 0; i < PRINT_COUNT; i++) {
                    System.out.print(printChar);
                    // 使用nextCondition唤醒下一个线程
                    // 因为只有一个线程在等待,所以signal或者signalAll都可以
                    nextCondtion.signal();
                    // 不是最后一次则通过thisCondtion等待被唤醒
                    // 必须要加判断,不然能够打印6次 但6次后就会直接死锁
                    if (i < PRINT_COUNT - 1) {
                        try {
                            // 本线程让出锁并等待唤醒
                            thisCondtion.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            } finally {
                // 释放打印锁
                reentrantLock.unlock();
            }
        }
    }
}

一个锁+一个状态位

思想:通过状态位来决定到哪个线程来打印。代码为:

package ConcurrentTest;

import org.junit.Test;

public class StateLockPrinter {
    //状态变量
    private volatile int state=0;
    @Test
    public void test() throws InterruptedException {
        //锁
        Object lock=new Object();
        ThreadGroup group=new ThreadGroup("xx");
        //打印A的线程
        Thread threadA=new Thread(group,new Printer(lock, 0,1, 'A'));
        //打印B的线程
        Thread threadB=new Thread(group,new Printer(lock, 1,2, 'B'));
        //打印C的线程
        Thread threadC=new Thread(group,new Printer(lock, 2,0, 'C'));
        //一次启动A B C线程
        threadA.start();
        Thread.sleep(1000);
        threadB.start();
        Thread.sleep(1000);
        threadC.start();
        //循环检查线程组合的活的线程数量
        while (group.activeCount()>0) {
            //让出CPU使用权
            Thread.yield();
        }
    }
    //打印线程
    private class Printer implements Runnable{
        //打印次数
        private static final int PRINT_COUNT=6;
        //打印锁
        private final Object printLock;
        //打印标志位 和state变量相关
        private final int printFlag;
        //后继线程的线程的打印标志位,state变量相关
        private final int nextPrintFlag;
        //该线程的打印字符
        private final char printChar;
        public Printer(Object printLock, int printFlag,int nextPrintFlag, char printChar) {
            super();
            this.printLock = printLock;
            this.printFlag=printFlag;
            this.nextPrintFlag=nextPrintFlag;
            this.printChar = printChar;
        }

        @Override
        public void run() {
            //获取打印锁 进入临界区
            synchronized (printLock) {
                //连续打印PRINT_COUNT次
                for(int i=0;i<PRINT_COUNT;i++){
                    //循环检验标志位 每次都阻塞然后等待唤醒
                    while (state!=printFlag) {
                        try {
                            printLock.wait();
                        } catch (InterruptedException e) {
                            return;
                        }
                    }
                    //打印字符
                    System.out.print(printChar);
                    //设置状态变量为下一个线程的标志位
                    state=nextPrintFlag;
                    //注意要notifyall,不然会死锁,因为notify只通知一个,
                    //但是同时等待的是两个,如果唤醒的不是正确那个就会没人唤醒,死锁了
                    printLock.notifyAll();
                }
            }
        }
    }
}

注:printLock.notifyAll()是随机唤醒,如果唤醒的不是下一个应该打印的线程,也没事,它会继续等待并释放锁。

参考的博文链接为:https://www.cnblogs.com/xiaoxi/p/8035725.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值