A.StringBuffer
B.StringBuilder
C.ArrayList
D.HashMap
参考答案
本题正确答案为A。
StringBuffer属于线程安全的类;StringBuilder、ArrayList、HashMap是非线程安全类。
参考答案
在工作中,可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理。产生数据的模块,则形象地称为生产者;而处理数据的模块,则称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模式。生产者和消费者模型的结构如图-1所示。
图-1
在实际工作中,典型的生产者消费者模型的案例是流媒体在线现在播放。流媒体下载是生产者;流媒体播放是消费者,如图-2所示。
图-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是截取控制台的部分数据。
图-3
从图-3的输出结果可以看出,只有下载数据完成后该数据才能播放,这是因为,BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个做取出操作。如果BlockingQueue对象是空的,则从BlockingQueue对象取数据的操作将会被阻塞进入等待状态,直到BlockingQueue对象有数据进入则被唤醒。同样,如果BlockingQueue对象是满的,任何试图向其存数据的操作也会被阻塞进入等待状态,直到BlockingQueue对象内有空间则会被唤醒继续操作,这样,BlockingQueue对象保证并发安全的同时提高了队列的存取效率。