查找表

1 下列属于线程安全类的是:

A.StringBuffer

B.StringBuilder

C.ArrayList

D.HashMap

参考答案

本题正确答案为A。

StringBuffer属于线程安全的类;StringBuilder、ArrayList、HashMap是非线程安全类。

2 使用BlockingQueue实现生产者和消费者模型

参考答案

在工作中,可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理。产生数据的模块,则形象地称为生产者;而处理数据的模块,则称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模式。生产者和消费者模型的结构如图-1所示。

图-1

在实际工作中,典型的生产者消费者模型的案例是流媒体在线现在播放。流媒体下载是生产者;流媒体播放是消费者,如图-2所示。

图-2

以下案例使用BlockingQueue实现生产者和消费者模型,模拟了流媒体下载视频数据和播放视频的过程。

实现此案例需要按照如下步骤进行。

步骤一:创建生产者

首先新建Donwload类,在该类中模拟视频数据下载的过程,其中BlockingQueue对象则是在上文中提到的缓冲区,Donwload对象负责向该缓冲区存储数据,代码如下所示:

 
  1. import java.util.concurrent.BlockingQueue;
  2. public class Donwload implements Runnable {
  3.     private final BlockingQueue<Object> queue;
  4.     public Donwload(BlockingQueue<Object> q) {
  5.         queue = q;
  6.     }
  7.     public void run() {
  8.         try {
  9.             while (true) {
  10.                 System.out.println("下载视频数据"+index);
  11.                 queue.put(produce());                
  12.             }
  13.         } catch (InterruptedException ex) {
  14.         }
  15.     }
  16.     int index=0;
  17.     public Object produce() {
  18.         try {
  19.             Thread.sleep(5000);
  20.         } catch (InterruptedException e) {
  21.             e.printStackTrace();
  22.         }
  23.         return "视频数据"+index++;
  24.     }
  25. }

步骤二:创建消费者

新建类Player,在该类中实现模拟播放视频的过程,其中BlockingQueue对象也是在上文中提到的缓冲区,Player对象负责从该缓冲区中取数据,代码如下所示:

 
  1. import java.util.concurrent.BlockingQueue;
  2. public class Player implements Runnable {
  3.     private final BlockingQueue<Object> queue;
  4.     public Player(BlockingQueue<Object> q) {
  5.         queue = q;
  6.     }
  7.     public void run() {
  8.         try {
  9.             while (true) {
  10.                 consume(queue.take());
  11.             }
  12.         } catch (InterruptedException ex) {
  13.         }
  14.     }
  15.     void consume(Object x) {
  16.         System.out.println("播放"+x);
  17.     }
  18. }

步骤三:启动线程

首先新建Setup类,在该类的main方法中,创建缓冲区BlockingQueue对象并将该对象传给下载器和播放器,这样就保证了下载器和播放器使用了相同的缓冲区。代码如下所示:

 
  1. import java.util.concurrent.ArrayBlockingQueue;
  2. import java.util.concurrent.BlockingQueue;
  3. public class Setup {
  4.     public static void main(String[] args) {
  5.         BlockingQueue<Object> q = new ArrayBlockingQueue<Object>(10);
  6.         Donwload p = new Donwload(q);
  7.         Player c1 = new Player(q);
  8.         Player c2 = new Player(q);
  9.         new Thread(p).start();
  10.         new Thread(c1).start();
  11.         new Thread(c2).start();
  12.     }
  13. }

从上述代码中,可以看出有一个线程负责下载,两个线程负责播放。

步骤四:运行

运行上述代码,由于程序在不断的运行,所以图-3是截取控制台的部分数据。

图-3

从图-3的输出结果可以看出,只有下载数据完成后该数据才能播放,这是因为,BlockingQueue内部使用两条队列,可允许两个线程同时向队列一个做存储,一个做取出操作。如果BlockingQueue对象是空的,则从BlockingQueue对象取数据的操作将会被阻塞进入等待状态,直到BlockingQueue对象有数据进入则被唤醒。同样,如果BlockingQueue对象是满的,任何试图向其存数据的操作也会被阻塞进入等待状态,直到BlockingQueue对象内有空间则会被唤醒继续操作,这样,BlockingQueue对象保证并发安全的同时提高了队列的存取效率。

原文地址:https://www.cnblogs.com/xyk1987/p/8330961.html