三线程打印ABC,循环十次的N种实现方式

编写一个程序,开启 3 个线程 A,B,C,这三个线程的输出分别为 A、B、C,每个线程将自己的 输出在屏幕上打印 10 遍,要求输出的结果必须按顺序显示。如:ABCABCABC....

核心在于多线程同步
完整代码:https://github.com/mightofcode/javacc

方法1,轮询AtomicInteger

缺点是轮询白耗CPU,性能很差

package com.mocyx.javacc.printer;

import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 轮询AtomicInteger实现交替输出ABC
 * @author Administrator
 */
@Component
public class PollingPrinter implements Printer {

    private final AtomicInteger atomicInteger = new AtomicInteger(0);

    class Worker implements Runnable {

        private String pstr;
        private int index;
        private int gap;
        private int max;

        public Worker(String pstr, int index, int gap, int max) {
            this.pstr = pstr;
            this.index = index;
            this.gap = gap;
            this.max = max;
        }

        @Override
        public void run() {
            while (true) {
                int v = atomicInteger.get();
                if (v == max) {
                    return;
                } else {
                    if (v % gap == index) {
                        System.out.print(pstr);
                        atomicInteger.set(v + 1);
                    }
                }
            }
        }
    }


    @Override
    public void print() {
        List<Thread> threads = new ArrayList<>();
        threads.add(new Thread(new Worker("A", 0, 3, 30)));
        threads.add(new Thread(new Worker("B", 1, 3, 30)));
        threads.add(new Thread(new Worker("C", 2, 3, 30)));
        for (Thread t : threads) {
            t.start();
        }
        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方法2,使用Lock & Condition同步

使用Lock & Condition要注意:
1 检查条件谓词,避免信号丢失和过早唤醒
2 注意在finally中进行unlock,否则出现异常会hang住

package com.mocyx.javacc.printer;

import org.springframework.stereotype.Component;

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

/**
 * 使用Lock and Condition进行同步
 *
 * @author Administrator
 */
@Component
public class SignalPrinter implements Printer {
    private final Lock lock = new ReentrantLock();
    private volatile int counter = 0;

    class Worker implements Runnable {

        Condition curCondition;
        Condition nextCondition;
        String pstr;
        int max;
        int index;
        int gap;

        public Worker(String pstr, int index, int gap, int max, Condition curCondition, Condition nextCondition) {
            this.pstr = pstr;
            this.max = max;
            this.curCondition = curCondition;
            this.nextCondition = nextCondition;
            this.index = index;
            this.gap = gap;
        }

        private boolean isMyTurn() {
            return counter % gap == index;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (!isMyTurn()) {
                        try {
                            curCondition.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (counter < max) {
                        System.out.print(pstr);
                    }
                    counter += 1;
                    nextCondition.signalAll();
                } finally {
                    lock.unlock();
                }
                if (counter >= max) {
                    return;
                }
            }
        }
    }

    @Override
    public void print() {
        List<Thread> threads = new ArrayList<>();
        List<Condition> conditions = new ArrayList<>();

        conditions.add(lock.newCondition());
        conditions.add(lock.newCondition());
        conditions.add(lock.newCondition());

        threads.add(new Thread(new Worker("A", 0, 3, 30, conditions.get(0), conditions.get(1))));
        threads.add(new Thread(new Worker("B", 1, 3, 30, conditions.get(1), conditions.get(2))));
        threads.add(new Thread(new Worker("C", 2, 3, 30, conditions.get(2), conditions.get(0))));

        for (Thread t : threads) {
            t.start();
        }

        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

方法3,使用Semphore进行同步

相比Lock & Condition,使用Semphore代码比较简洁,不容易出错

package com.mocyx.javacc.printer;

import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * @author Administrator
 */
@Component
public class SemaphorePrinter implements Printer {



    class Worker implements Runnable {
        private String pstr;

        private Semaphore curSemphore;
        private Semaphore nextSemphore;
        private int count = 0;

        Worker(String pstr, int count, Semaphore curSemphore, Semaphore nextSemphore) {
            this.pstr = pstr;
            this.count = count;
            this.curSemphore = curSemphore;
            this.nextSemphore = nextSemphore;
        }

        @Override
        public void run() {
            for (int i = 0; i < count; i++) {
                try {
                    curSemphore.acquire(1);
                    System.out.print(pstr);
                    nextSemphore.release(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void print() {
        List<Thread> threads = new ArrayList<>();
        List<Semaphore> semaphores = new ArrayList<>();

        semaphores.add(new Semaphore(0));
        semaphores.add(new Semaphore(0));
        semaphores.add(new Semaphore(0));

        threads.add(new Thread(new Worker("A", 10, semaphores.get(0), semaphores.get(1))));
        threads.add(new Thread(new Worker("B", 10, semaphores.get(1), semaphores.get(2))));
        threads.add(new Thread(new Worker("C", 10, semaphores.get(2), semaphores.get(0))));

        for (Thread t : threads) {
            t.start();
        }

        semaphores.get(0).release(1);

        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方法4,使用BlockingQueue进行同步

思路跟go channel类似,通过BlockingQueue传递信息

package com.mocyx.javacc.printer;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * 使用阻塞队列进行同步
 *
 * @author Administrator
 */
public class QueuePrinter implements Printer {


    static class Msg {
        public static final Msg PRINT_SUCCESS = new Msg();
        public static final Msg PRINT = new Msg();
        public static final Msg QUIT = new Msg();
    }

    class Channel {
        BlockingQueue<Msg> inQueue = new ArrayBlockingQueue<Msg>(100);
        BlockingQueue<Msg> outQueue = new ArrayBlockingQueue<Msg>(100);
    }

    class Worker implements Runnable {

        Channel inChannel;
        String pstr;

        Worker(String pstr, Channel inChannel) {
            this.inChannel = inChannel;
            this.pstr = pstr;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Msg msg = inChannel.inQueue.take();
                    if (msg == Msg.PRINT) {
                        System.out.print(pstr);
                        inChannel.outQueue.put(Msg.PRINT_SUCCESS);
                    } else if (msg == Msg.QUIT) {
                        return;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void print() {
        List<Thread> threads = new ArrayList<>();

        List<Channel> channels = new ArrayList<>();
        channels.add(new Channel());
        channels.add(new Channel());
        channels.add(new Channel());


        threads.add(new Thread(new Worker("A", channels.get(0))));
        threads.add(new Thread(new Worker("B", channels.get(1))));
        threads.add(new Thread(new Worker("C", channels.get(2))));

        for (Thread t : threads) {
            t.start();
        }

        for (int i = 0; i < 30; i++) {
            try {
                channels.get(i % channels.size()).inQueue.put(Msg.PRINT);
                channels.get(i % channels.size()).outQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (Channel c : channels) {
            c.inQueue.add(Msg.QUIT);
        }

        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

方法0,Sleep同步法

这个方法确实能工作,但是可能会被面试官打

package com.mocyx.javacc.printer;


import java.util.ArrayList;
import java.util.List;

/**
 * sleep and sleep
 *
 * @author Administrator
 */
public class SleepPrinter implements Printer {

    static class Worker implements Runnable {
        private String printStr;
        private int sleepGap;
        private int delay;
        private int count;

        public Worker(String printStr,  int delay, int sleepGap, int count) {
            this.printStr = printStr;
            this.sleepGap = sleepGap;
            this.delay = delay;
            this.count = count;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(delay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < count; i++) {
                try {
                    Thread.sleep(sleepGap);
                    System.out.print(printStr);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }



    @Override
    public void print() {
        List<Thread> threads = new ArrayList<>();
        threads.add(new Thread(new Worker("A", 00, 30, 10)));
        threads.add(new Thread(new Worker("B", 10, 30, 10)));
        threads.add(new Thread(new Worker("C", 20, 30, 10)));
        for (Thread t : threads) {
            t.start();
        }
        try {
            for (Thread t : threads) {
                t.join();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354