1 下列属于线程安全类的是:
A.StringBuffer
B.StringBuilder
C.ArrayList
D.HashMap
参考答案
本题正确答案为A。
StringBuffer属于线程安全的类;StringBuilder、ArrayList、HashMap是非线程安全类。
2 使用BlockingQueue实现生产者和消费者模型
参考答案
在工作中,可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理。产生数据的模块,则形象地称为生产者;而处理数据的模块,则称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模式。生产者和消费者模型的结构如图-1所示。
![](http://tts.tmooc.cn/ttsPage/JAVA/JSD_N_V08/JAVASE02/DAY05/EXERCISE/01/index.files/image001.png)
图-1
在实际工作中,典型的生产者消费者模型的案例是流媒体在线现在播放。流媒体下载是生产者;流媒体播放是消费者,如图-2所示。
![](http://tts.tmooc.cn/ttsPage/JAVA/JSD_N_V08/JAVASE02/DAY05/EXERCISE/01/index.files/image002.png)
图-2
以下案例使用BlockingQueue实现生产者和消费者模型,模拟了流媒体下载视频数据和播放视频的过程。
实现此案例需要按照如下步骤进行。
步骤一:创建生产者
首先新建Donwload类,在该类中模拟视频数据下载的过程,其中BlockingQueue对象则是在上文中提到的缓冲区,Donwload对象负责向该缓冲区存储数据,代码如下所示:
- import java.util.concurrent.BlockingQueue;
- public class Donwload implements Runnable {
- private final BlockingQueue<Object> queue;
- public Donwload(BlockingQueue<Object> q) {
- queue = q;
- }
- public void run() {
- try {
- while (true) {
- System.out.println("下载视频数据"+index);
- queue.put(produce());
- }
- } catch (InterruptedException ex) {
- }
- }
- int index=0;
- public Object produce() {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "视频数据"+index++;
- }
- }
步骤二:创建消费者
新建类Player,在该类中实现模拟播放视频的过程,其中BlockingQueue对象也是在上文中提到的缓冲区,Player对象负责从该缓冲区中取数据,代码如下所示:
- import java.util.concurrent.BlockingQueue;
- public class Player implements Runnable {
- private final BlockingQueue<Object> queue;
- public Player(BlockingQueue<Object> q) {
- queue = q;
- }
- public void run() {
- try {
- while (true) {
- consume(queue.take());
- }
- } catch (InterruptedException ex) {
- }
- }
- void consume(Object x) {
- System.out.println("播放"+x);
- }
- }
步骤三:启动线程
首先新建Setup类,在该类的main方法中,创建缓冲区BlockingQueue对象并将该对象传给下载器和播放器,这样就保证了下载器和播放器使用了相同的缓冲区。代码如下所示:
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.BlockingQueue;
- public class Setup {
- public static void main(String[] args) {
- BlockingQueue<Object> q = new ArrayBlockingQueue<Object>(10);
- Donwload p = new Donwload(q);
- Player c1 = new Player(q);
- Player c2 = new Player(q);
- new Thread(p).start();
- new Thread(c1).start();
- new Thread(c2).start();
- }
- }
从上述代码中,可以看出有一个线程负责下载,两个线程负责播放。
步骤四:运行
运行上述代码,由于程序在不断的运行,所以图-3是截取控制台的部分数据。
![](http://tts.tmooc.cn/ttsPage/JAVA/JSD_N_V08/JAVASE02/DAY05/EXERCISE/01/index.files/image003.png)
图-3
从图-3的输出结果可以看出,只有下载数据完成后该数据才能播放,这是因为,BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个做取出操作。如果BlockingQueue对象是空的,则从BlockingQueue对象取数据的操作将会被阻塞进入等待状态,直到BlockingQueue对象有数据进入则被唤醒。同样,如果BlockingQueue对象是满的,任何试图向其存数据的操作也会被阻塞进入等待状态,直到BlockingQueue对象内有空间则会被唤醒继续操作,这样,BlockingQueue对象保证并发安全的同时提高了队列的存取效率。