今天在群里面看到一个很有意思的面试题:
“编写两个线程,一个线程打印1~25,另一个线程打印字母A~Z,打印顺序为12A34B56C……5152Z,要求使用线程间的通信。”
这是一道非常好的面试题,非常能彰显被面者关于多线程的功力,一下子就勾起了我的兴趣。这里抛砖引玉,给出7种想到的解法。
1. 第一种解法,包含多种小的不同实现方式,但一个共同点就是靠一个共享变量来做控制;
a. 利用最基本的synchronized、notify、wait:
|
|
b. 利用Lock和Condition:
|
|
c. 利用volatile:
volatile修饰的变量值直接存在main memory里面,子线程对该变量的读写直接写入main memory,而不是像其它变量一样在local thread里面产生一份copy。volatile能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。
|
|
d. 利用AtomicInteger:
|
|
2. 第二种解法,是利用CyclicBarrierAPI;
CyclicBarrier可以实现让一组线程在全部到达Barrier时(执行await()),再一起同时执行,并且所有线程释放后,还能复用它,即为Cyclic。
CyclicBarrier类提供两个构造器:
|
|
这里是利用它到达Barrier后去执行barrierAction。
|
|
这里多说一点,这个API其实还是利用lock和condition,无非是多个线程去争抢CyclicBarrier的instance的lock罢了,最终barrierAction执行时,是在抢到CyclicBarrierinstance的那个线程上执行的。
3. 第三种解法,是利用PipedInputStreamAPI;
这里用流在两个线程间通信,但是Java中的Stream是单向的,所以在两个线程中分别建了一个input和output。这显然是一种很搓的方式,不过也算是一种通信方式吧……-_-T,执行的时候那种速度简直。。。请不要BS我。
|
|
4. 第四种解法,是利用BlockingQueue;
顺便总结下BlockingQueue的一些内容。
BlockingQueue定义的常用方法如下:
-
add(Object):把Object加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则抛出异常。 -
offer(Object):表示如果可能的话,将Object加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。 -
put(Object):把Object加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。 -
poll(time):获取并删除BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。当不传入time值时,立刻返回。 -
peek():立刻获取BlockingQueue里排在首位的对象,但不从队列里删除,如果队列为空,则返回null。 -
take():获取并删除BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。
BlockingQueue有四个具体的实现类:
-
ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。 -
LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。 -
PriorityBlockingQueue:类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。 -
SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。
这里我用了两种玩法:
- 一种是共享一个queue,根据
peek和poll的不同来实现; - 第二种是两个queue,利用
take()会自动阻塞来实现。
|
|
本文所有代码已上传至GitHub:https://github.com/EdisonXu/POC/tree/master/concurrent-test
本文通过一个面试题探讨了Java中线程间的通信方法,提供了包括synchronized、Lock、CyclicBarrier在内的7种不同实现方案。

2084

被折叠的 条评论
为什么被折叠?



