多线程依次打印ABC

Posted by 令德湖周杰伦 on 04-09,2024

1. 题目:

给3个线程分别交替打印A、B、C,10次,
预期结果为:
thread1: A
thread2: B
thread3: C
thread1: A
thread2: B
thread3: C
thread1: A
thread2: B
thread3: C
...

2. 解法

2.1 使用synchronized

思路:

  1. 使用synchronized给某个对象上锁
  2. 线程的打印顺序使用一个变量n%3来控制,0 代表线程打印A, 1代表线程打印B, 2代表线程打C
  3. 打印完后,通知其他线程解锁
public void solutionBySync(){
        SyncObject syncObject = new SyncObject();
        new SyncThread(syncObject,"A").start();
        new SyncThread(syncObject,"B").start();
        new SyncThread(syncObject,"C").start();
    }

    class SyncObject{

    }
    private int num = 0;

    class SyncThread extends Thread{
        private SyncObject sync;
        private String name;
        private String[] arr = {"A","B","C"};

        public SyncThread(SyncObject syncObject,String name){
            this.sync = syncObject;
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i <10 ; i++) {
                synchronized (sync){
                    if(name.equals(arr[num%3])){
                        System.out.println(name);
                        num ++;
                        sync.notifyAll();
                    }else{
                        try {
                            sync.wait();
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

2.2 通过CAS

思路:

  1. 使用cas 在while中自旋等待获取锁
  2. 线程的打印顺序使用一个变量n%3来控制,0 代表线程打印A, 1代表线程打印B, 2代表线程打C
  3. 打印完后,brerk 当前while,进入下一轮打印
    public void solutionByCAS(){
        AtomicInteger num = new AtomicInteger(0);
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.submit(new CASThread("A",num));
        executorService.submit(new CASThread("B",num));
        executorService.submit(new CASThread("C",num));
        executorService.shutdown();
    }

    class CASThread implements Runnable{
        private String name;
        private AtomicInteger num;

        public CASThread(String name, AtomicInteger num){
            this.name = name;
            this.num = num;
        }

        private String[] arr = {"A","B","C"};

        @Override
        public void run() {
            for (int i = 0; i <10 ; i++) {
                while(true){
                    if(name.equals(arr[num.get()%3])){
                        System.out.println(name);
                        num.getAndIncrement();
                        break;
                    }
                }
            }
            System.out.println("over:"+name);
        }
    }

2.3 使用Semaphore

思路:

  1. 设置3个信号量,其中打印a的信号量初识值设置为1
  2. 打印a的线程,先消耗掉1个信号量后开始打印A,然后给打印b的信号量释放1个量
  3. 打印b的线程,先消耗掉1个信号量后开始打印B,然后给打印C的信号量释放1个量
  4. 打印c的线程,先消耗掉1个信号量后开始打印C,然后给打印A的信号量释放1个量
  5. 周而复始,打印10遍
    public void solutionBySemaphore(){
        Semaphore sa = new Semaphore(1);
        Semaphore sb = new Semaphore(0);
        Semaphore sc = new Semaphore(0);

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {

                    try {
                        sa.acquire(1);
                        System.out.println("A");
                        sb.release(1);
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {

                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    try {
                        sb.acquire(1);
                        System.out.println("B");
                        sc.release(1);
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {

                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    try {
                        sc.acquire(1);
                        System.out.println("C");
                        sa.release(1);
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {

                    }
                }
            }
        }).start();
    }

2.4 使用ReentrantLock

思路:

  1. 对一个可重入锁设置3个条件
  2. 线程的打印顺序使用一个变量k来控制,0 代表线程打印A, 1代表线程打印B, 2代表线程打C
  3. 打印线程你如后如果还轮不到自己打印那么就await();等待其他线程唤醒signal();
  4. 打印a的线程 唤醒 打印b的线程,打印b的线程 唤醒 打印c的线程,打印c的线程 唤醒 打印a的线程
  5. 周而复始,打印10次
    private int k = 1;
    public void solutionByLock() {

        ReentrantLock lock = new ReentrantLock();
        Condition ca = lock.newCondition();
        Condition cb = lock.newCondition();
        Condition cc = lock.newCondition();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    lock.lock();
                    try {
                        if(k != 1){
                            ca.await();
                        }
                        System.out.println("A");
                        k=2;
                        cb.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    lock.lock();
                    try {
                        if(k!=2){
                            cb.await();
                        }
                        System.out.println("B");
                        k=3;
                        cc.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    lock.lock();
                    try {
                        if(k!=3){
                            cc.await();
                        }
                        System.out.println("C");
                        k=1;
                        ca.signal();
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        lock.unlock();
                    }
                }
            }
        }).start();
    }