三个线程顺序打印ABC10次实现方案总结

错误示例

public class PrintABCUsingSingleThreadExecutor {

    // 这种方式其实不是三个线程轮流输出,只是线程池的线程在输出
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            System.out.print(Thread.currentThread().getId()+"A");
        });

        Thread threadB = new Thread(() -> {
            System.out.print(Thread.currentThread().getId()+"B");
        });

        Thread threadC = new Thread(() -> {
            System.out.println(Thread.currentThread().getId()+"C");
        });
        // 单线程线程池输出
        ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            singleExecutor.execute(threadA);
            singleExecutor.execute(threadB);
            singleExecutor.execute(threadC);
        }
        //关闭线程池
        singleExecutor.shutdown();

    }
}

从输出结果看出是同一个线程在输出 

实现思路:三个线程顺序输出ABC,类似顺序打开ABC三间房,B的钥匙在A那,C的钥匙在B那,A的钥匙在C那,同时开个后门> 开始的时候A门可以打开,不需要钥匙,后面就都是凭钥匙开门保证顺序。

方式一:synchronized + wait + notify

public class PrintABCUsingWaitNotify {
    private int times;
    private volatile int state;
    private static final Object LOCK = new Object();

    public PrintABCUsingWaitNotify(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingWaitNotify printABC = new PrintABCUsingWaitNotify(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() {
        try {
            print("A", 0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printB() {
        try {
            print("B", 1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC() {
        try {
            print("C", 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void print(String name, int targetState) throws InterruptedException {
        for (int i = 0; i < times; i++) {
            synchronized (LOCK) {
                while (state % 3 != targetState) {
                    LOCK.wait();
                }
                state++;
                System.out.print(name);
                LOCK.notifyAll();
            }
        }
    }
}

方式二:ReentrantLock

public class PrintABCUsingLock {
    private int times;
    private int state;
    private Lock lock = new ReentrantLock();

    public PrintABCUsingLock(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingLock printABC = new PrintABCUsingLock(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() {
        print("A", 0);
    }

    public void printB() {
        print("B", 1);
    }

    public void printC() {
        print("C", 2);
    }

    private void print(String name, int targetState) {
        for (int i = 0; i < times; ) {
            lock.lock();
            if (state % 3 == targetState) {
                state++;
                i++;
                System.out.print(name);
            }
            lock.unlock();
        }
    }
}

方式三:ReentrantLock + Condition

public class PrintABCUsingLockCondition {
    private int times;
    private int state;
    private Lock lock = new ReentrantLock();
    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();
    private Condition conditionC = lock.newCondition();

    public PrintABCUsingLockCondition(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingLockCondition printABC = new PrintABCUsingLockCondition(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() {
        print("A", 0, conditionA, conditionB);
    }

    public void printB() {
        print("B", 1, conditionB, conditionC);
    }

    public void printC() {
        print("C", 2, conditionC, conditionA);
    }

    private void print(String name, int targetState, Condition current,
            Condition next) {
        for (int i = 0; i < times;) {
            lock.lock();
            try {
                while (state % 3 != targetState) {
                    current.await();
                }
                state++;
                i++;
                System.out.print(name);
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

方式四:LockSupport

public class PrintABCUsingLockSupport {

    static Thread t1, t2, t3;

    public static void main(String[] args) {
        t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.print("A");
                LockSupport.unpark(t2);
                LockSupport.park();
            }
        });

        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    LockSupport.park();
                    System.out.print("B");
                    LockSupport.unpark(t3);
                }
            }
        });

        t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    LockSupport.park();
                    System.out.println("C");
                    LockSupport.unpark(t1);
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

方式五:CountDownLatch 

package com.example.spring_boot_study.java8;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 题目要求:ABC三个线程顺序执行10次
 * 实现思路:使用 CountDownLatch 来实现:
 * (1)定义dependLatch(所依赖的latch名)、selfLatch(自己的latch名)
 * (2)首先调用所依赖的latch的await()方法,如果所依赖的latch的count为0,则重置所依赖的latch并打印需要输出的,最后将自身的count减去
 * (3)sum为需要执行的次数
 */
public class PrintABCUsingCountDownLatch implements Runnable {

    private static Map<String, CountDownLatch> countDownLatchMap = new HashMap<>();

    private String dependLatch;
    private String selfLatch;

    private PrintABCUsingCountDownLatch(String dependLatch, String selfLatch) {
        this.dependLatch = dependLatch;
        this.selfLatch = selfLatch;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                countDownLatchMap.get(dependLatch).await();
                countDownLatchMap.put(dependLatch, new CountDownLatch(1));

                System.out.println(Thread.currentThread().getName() + ":" + selfLatch);

                countDownLatchMap.get(selfLatch).countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        String latchA = "A";
        String latchB = "B";
        String latchC = "C";

        countDownLatchMap.put(latchA, new CountDownLatch(1));
        countDownLatchMap.put(latchB, new CountDownLatch(1));
        countDownLatchMap.put(latchC, new CountDownLatch(1));

        //创建三个线程,但是此时由于三个CountDownLatch都不为0,所以三个线程都处于阻塞状态
        Thread threadA = new Thread(new PrintABCUsingCountDownLatch(latchC, latchA));
        Thread threadB = new Thread(new PrintABCUsingCountDownLatch(latchA, latchB));
        Thread threadC = new Thread(new PrintABCUsingCountDownLatch(latchB, latchC));
        threadA.start();
        threadB.start();
        threadC.start();
        //latchC 阻塞了 latchA;调用latchC的countDown()方法,先让latchC为0,使latchA先运行
        countDownLatchMap.get(latchC).countDown();
    }
}

方式六:Semaphore

package com.tiger.thread;

import java.util.concurrent.Semaphore;

public class PrintABCUsingSemaphore {
    private int times;
    // 注意构造入参0和1的区别;这里也全部使用1实现
    // 当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。也就是说参数为0,即表示调用了 acquire()。
    private Semaphore semaphoreA = new Semaphore(1);
    private Semaphore semaphoreB = new Semaphore(0);
    private Semaphore semaphoreC = new Semaphore(0);

    public PrintABCUsingSemaphore(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingSemaphore printABC = new PrintABCUsingSemaphore(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() {
        try {
            print("A", semaphoreA, semaphoreB);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printB() {
        try {
            print("B", semaphoreB, semaphoreC);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC() {
        try {
            print("C", semaphoreC, semaphoreA);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void print(String name, Semaphore current, Semaphore next)
            throws InterruptedException {
        for (int i = 0; i < times; i++) {
            current.acquire();
            if ("C".equals(name)){
                System.out.println(name);
            }else {
                System.out.print(name);
            }
            next.release();
        }
    }
}

方式七:CyclicBarrier

package com.tiger.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class PrintABCUsingCyclicBarrier {
    private int times;

    public PrintABCUsingCyclicBarrier(int times) {
        this.times = times;
    }

    CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    CyclicBarrier cyclicBarrier2 = new CyclicBarrier(3);
    CyclicBarrier cyclicBarrier3 = new CyclicBarrier(3);

    public static void main(String[] args) {
        final PrintABCUsingCyclicBarrier printABC = new PrintABCUsingCyclicBarrier(10);

        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();

    }

    private void printA() {
        try {
            print("A", cyclicBarrier, cyclicBarrier2, cyclicBarrier3);
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private void printB() {
        try {
            print("B", cyclicBarrier, cyclicBarrier2, cyclicBarrier3);
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private void printC() {
        try {
            print("C", cyclicBarrier, cyclicBarrier2, cyclicBarrier3);
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    private void print(String word, CyclicBarrier cyclicBarrier, CyclicBarrier cyclicBarrier2, CyclicBarrier cyclicBarrier3) throws BrokenBarrierException, InterruptedException {
        for (int i = 0; i < times; i++) {
            cyclicBarrier.await();
            if ("A".equals(word)) {
                System.out.print(word);
            }
            cyclicBarrier2.await();
            if ("B".equals(word)) {
                System.out.print(word);
            }
            cyclicBarrier3.await();
            if ("C".equals(word)) {
                System.out.println(word);
            }
        }
    }

}


方式八: Volatile

public class PrintABCUsingVolatile {
    private int times;
    private volatile int state;

    public PrintABCUsingVolatile(int times) {
        this.times = times;
    }

    public static void main(String[] args) {
        PrintABCUsingVolatile printABC = new PrintABCUsingVolatile(10);
        // 这里不考虑线程实际获取时间分片的问题,默认按代码顺序获取时间分片
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() { print("A", 0); }

    public void printB() {
        print("B", 1);
    }

    public void printC() {
        print("C", 2);
    }

    private void print(String name, int targetState) {
        for (int i = 0; i < times; ) {
            // 不加锁无法保证if代码块的原子性
            while (state % 3 != targetState) {
            }
            if ("C".equals(name)){
                System.out.println(name);
            }else {
                System.out.print(name);
            }
            state++;
            i++;

        }
    }
}

方式九:AtomicInteger(跟方式八区别不大)

public class PrintABCUsingAtomicInteger {

    private int times;

    public PrintABCUsingAtomicInteger(int times) {
        this.times = times;
    }

    private static AtomicInteger state = new AtomicInteger(0);

    public static void main(String[] args) {
        PrintABCUsingAtomicInteger printABC = new PrintABCUsingAtomicInteger(10);
        new Thread(printABC::printA).start();
        new Thread(printABC::printB).start();
        new Thread(printABC::printC).start();
    }

    public void printA() {
        print("A", 0);
    }

    public void printB() {
        print("B", 1);
    }

    public void printC() {
        print("C", 2);
    }

    private void print(String name, int targetState) {
        for (int i = 0; i < times; ) {
            // 不加锁无法保证if代码块的原子性
            while (state.get() % 3 != targetState) {
            }
            if ("C".equals(name)) {
                System.out.println(name);
            } else {
                System.out.print(name);
            }
            state.incrementAndGet();
            i++;

        }
    }
}

方式十:BlockingQueue

package com.tiger.thread;

import java.util.concurrent.*;

public class PrintABCUsingBlockingQueue {

    private int times;

    public PrintABCUsingBlockingQueue(int times) {
        this.times = times;
    }

    private static BlockingQueue blockingQueue = new ArrayBlockingQueue(30);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            blockingQueue.add("A");
            blockingQueue.add("B");
            blockingQueue.add("C");
        }
        PrintABCUsingBlockingQueue p = new PrintABCUsingBlockingQueue(10);
        System.out.println(blockingQueue.size());

        
        new Thread(p::printA).start();
        new Thread(p::printB).start();
        new Thread(p::printC).start();

    }

    private void printA() {
        try {
            while (blockingQueue.size() != 0) {
                if (blockingQueue.size() % 3 == 0) {
                    System.out.print("A");
                    // 注意 System.out.print("A")需要放在Object poll = blockingQueue.poll()之前,否则可能乱序
                    Object poll = blockingQueue.poll();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void printB() {
        try {
            while (blockingQueue.size() != 0) {
                if (blockingQueue.size() % 3 == 2) {
                    System.out.print("B");
                    Object poll = blockingQueue.poll();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void printC() {
        try {
            while (blockingQueue.size() != 0) {
                if (blockingQueue.size() % 3 == 1) {
                    System.out.println("C");
                    Object poll = blockingQueue.poll();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方式十一:Exchanger

package com.tiger.thread;

import java.util.concurrent.Exchanger;

public class PrintABCUsingExchanger {
    private int times;

    public PrintABCUsingExchanger(int times) {
        this.times = times;
    }

    private static Exchanger<Integer> exchangerA = new Exchanger<>();
    private static Exchanger<Integer> exchangerB = new Exchanger<>();
    private static Exchanger<Integer> exchangerC = new Exchanger<>();

    public void printA() {
        for (int i = 0; i < times; i++) {
            try {
                // 确保从A开始数据的传递
                if (i == 0) {
                    System.out.print("A");
                } else {
                    Integer state = exchangerC.exchange(0);
                    while (state != 3) {
                    }
                    System.out.print("A");
                }
                //交换
                exchangerA.exchange(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void printB() {
        try {
            for (int i = 0; i < times; i++) {
                //交换
                Integer state = exchangerA.exchange(0);
                //等待
                while (state != 1) {
                }
                //执行
                System.out.print("B");
                //第二次交换
                exchangerB.exchange(2);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void printC() {
        try {
            for (int i = 0; i < times; i++) {
                Integer state = exchangerB.exchange(0);
                while (state != 2) {
                }
                System.out.println("C");
                // 最后一次循环不需要传递数据了,否则线程无法结束
                if (i != times - 1) {
                    exchangerC.exchange(3);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PrintABCUsingExchanger p = new PrintABCUsingExchanger(10);
        new Thread(p::printA).start();
        new Thread(p::printB).start();
        new Thread(p::printC).start();
    }
}

參考:

ABC三个线程顺序输出的问题

三个线程轮流执行顺序打印ABC

三个线程顺序打印ABC?我有十二种做法!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用信号量来实现线程的同步,代码如下: ```python import threading class ABCPrinter: def __init__(self): self.sem_a = threading.Semaphore(1) self.sem_b = threading.Semaphore(0) self.sem_c = threading.Semaphore(0) def print_a(self): for i in range(10): self.sem_a.acquire() print('A', end='') self.sem_b.release() def print_b(self): for i in range(10): self.sem_b.acquire() print('B', end='') self.sem_c.release() def print_c(self): for i in range(10): self.sem_c.acquire() print('C', end='') self.sem_a.release() printer = ABCPrinter() thread_a = threading.Thread(target=printer.print_a) thread_b = threading.Thread(target=printer.print_b) thread_c = threading.Thread(target=printer.print_c) thread_a.start() thread_b.start() thread_c.start() thread_a.join() thread_b.join() thread_c.join() ``` 每个线程都会通过 acquire() 方法等待信号量,当信号量的值大于 0 时,线程会继续执行,同时将信号量的值减 1。当线程执行完任务后,会通过 release() 方法将信号量的值加 1。在这个例子中,每个线程都有一个信号量,用来控制打印顺序。初始时,A 线程的信号量为 1,B 和 C 线程的信号量为 0,因此 A 线程会先执行。当 A 线程打印完后,会将 B 线程的信号量加 1,然后等待自己的信号量被释放。B 线程接着执行,打印完后将 C 线程的信号量加 1,然后等待自己的信号量被释放。C 线程接着执行,打印完后将 A 线程的信号量加 1,然后等待自己的信号量被释放。依此类推,直到所有线程打印十次才结束。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值