最近公司新入职了几个妹子,其中有一个妹子不停的追着我问各种问题。这次又来问我关于线程通信相关的问题。线程之间的通信以及线程之间的协作方面的面试,通常是考验一个Java程序员多线程方面的基本功。为了让妹子搞清楚线程之间的通信那些事儿,我用了8种方法!
妹子们面试的时候是否都遇到过交替打印数字字母的面试题。下面我们以交替打印12A34B…来说明线程间通信的几种方式.
分别是
- 使用synchronized,wait,notify,notifyAll
- Lock,Condition
- LockSupport
- 使用volatile关键字
- AtomicInteger
- 利用 Piped Stream
- 使用CyclicBarrier
- 利用BlockingQueue
1 使用synchronized,wait,notify,notifyAll
使用synchronized 等方法来控制共享变量,完成交替打印。
思路:
-
在同步方法中先判断信号量,如果不是当前需要的信号使用wait()阻塞线程。
-
完成打印之后切换信号变量。再唤醒所有线程。
1 |
|
2 Lock,Condition
通过使用Lock,Condition的signal() 和 await()来进行换新阻塞交替打印。
1 |
|
3 LockSupport
LockSupport 用来创建锁和其他同步类的基本线程阻塞。当调用LockSupport.park时,表示当前线程将会等待,直至获得许可,当调用LockSupport.unpark时,必须把等待获得许可的线程作为参数进行传递,好让此线程继续运行。
其中:
- park函数,阻塞线程,并且该线程在下列情况发生之前都会被阻塞: ① 调用unpark函数,释放该线程的许可。② 该线程被中断。③ 设置的时间到了。并且,当time为绝对时间时,isAbsolute为true,否则,isAbsolute为false。当time为0时,表示无限等待,直到unpark发生。
- unpark函数,释放线程的许可,即激活调用park后阻塞的线程。这个函数不是安全的,调用这个函数时要确保线程依旧存活。
1 |
|
4 volatile
根据volatile修饰的对象在JVM内存中的可见性,完成交替打印
1 |
|
5 AtomicInteger
同样利用了AtomicInteger的并发特性,来完成交替打印。
1 |
|
6 利用 Piped Stream
使用Stream中的Piped Stream分别控制输出,但是其运行速度极慢。
1 |
|
7 利用BlockingQueue
BlockingQueue 通常用于一个线程生产对象,另外一个线程消费这些对象的场景。
一个线程负责往里面放,另一个线程从里面取一个BlockingQueue。
线程可以持续将新对象插入到队列之中,直到队列达到可容纳的临界点。当队列到达临界点之后,线程生产者会在插入对象是进入阻塞状态,直到有另外一个线程从队列中拿走一个对象。消费线程会不停的从队列中拿出对象。如果消费线程从一个空的队列中获取对象的话,那么消费线程会处阻塞状态,直到一个生产线程把对象丢进队列。
BlockingQueue常用方法如下:
那么我们使用一个LinkedBlockingQueue来完成开始出现的题目
方法中我们使用offer,peek,poll这几个方法来完成。
1 |
|
我们也可以使用两个LinkedBlockinQueue来完成,分别使用带阻塞的put,take来完成。代码如下
1 |
|
8 使用CyclicBarrier
CyclicBarrier的字面意思就是可循环使用的屏障,它可以让一组线程到达一个阻塞点(屏障)时被阻塞。直到最后一个线程到达阻塞点后,屏障才会开门,然后所有被拦截的线程就可以继续运行。
CyclicBarrier中有一个barrierCommand,主要就是在所有线程到达阻塞点之后执行的一个线程。可以使用构造方法来 CyclicBarrier(int parties, Runnable barrierAction)进行构建。
关于使用CyclicBarrier进行交替打印,先来说一下思路。
- 利用await()方法使得每循环一次都阻塞线程。
- 将每次循环输出的值放到一个共享的同步list里面。
- 然后再使用barrierAction到达阻塞点之后进行输出。 由于list里面的值先后顺序有变化,所有先排序然后再打印。
下面我们看一下实操代码:
1 |
|
Demo代码GitHub: https://github.com/javatechnorth/java-study-note/tree/master/multiThread/src/main/java/org/javanorth/currency/signal
欢迎来瓢!!
总结
一口气给妹子讲了8种方法来完成交替打印的编程题目,总结一下线程通信的内容。
线程间通信大致可以分为3种:
- 通过共享对象,即共享内存进行通信
- 等待通知机制,(利用wait(),notify(),notifyAll(),Lock,以及AQS等)
- 利用管道输入,输出流。
相信妹子已经比较比较了解线程间通信的内容。由于妹子的问题比较多,后面哥慢慢给你讲!敬请期待。