前段时间,做了个“双缓冲队列”,可是测试的效果就是不怎么明显,理论完全都在这里,可是就是看不到效果。
昨天在胡总的提示下,终于意识到不该用阻塞队列,换成普通的List对象,这样效果就明显多啦~~
又重新写了一篇文档,如下
提出问题:为啥要有双缓冲队列?
引用09年9月《程序员》上的一句话:双缓冲队列就是冲着同步/互斥的开销来的。我们知道,在多个线程并发访问同一个资源的时候,需要特别注意线程的同步问题。稍稍不注意,哦活,程序结果不正确了。最经典的就是“银行取钱”的例子,想想,都跟现金挂上钩了,看来这真不容忽视。
今天我们要谈的不是如何去给资源加锁解锁来解决同步问题,今天的重点在于,如何将线程同步的开销降低到我们力所能及的程度。如果你觉得,你可以通过增加硬件资源来弥补程序开销,那么,你将不可能成为一个优秀的程序员。
进入正题,先引入一个例子,两个实体:一个是玩具工厂,它的工作就是不停地生产玩具;另外一个实体就是小孩,它的工作就是不停地从工厂拿玩具。小孩不可能直接到工厂去“拿”玩具吧?呵呵,妈妈是绝对不会放心的。所以,我们有一个“搬运工”,搬运工自然要具备“存放”的功能,不然他怎么将玩具带给小孩呢,是吧。所以,我们先将搬运工定义为一个List,用来存放工厂生产出来的玩具。
代码如下
玩具类,定义一个玩具实体
public class Toy {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
接下来是玩具工厂,它得“不停地”生产,所以,把它设计成一个线程类
玩具工厂,将玩具对象,放到list中
public class Factory extends Thread{
public void run(){
while(true){
Toy t = new Toy ();
t.setName("玩具");
synchronized (Tools.lT){
Tools.lT.add(t);
}
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
注意到,在上面这段代码当中,必须得用synchronized (Tools.lT)将Tools.lT给加锁。不然会出现线程同步问题(开销就在这里)。
再接下来,看看小孩是怎么“玩”玩具的:
小孩类,从list中取出玩具对象
public class Kid extends Thread {
long time1 = System.currentTimeMillis();
int count = 0;
public void run() {
while(true){
synchronized(Tools.lT){
if(Tools.lT.size()!=0)
Tools.lT.remove(0);
}
count++;
if(count==100000){
javax.swing.JOptionPane.showMessageDialog(null, "用时间: "+(System.currentTimeMillis()-time1));
System.exit(0);
}
}
}
}
当list不为空的时候,将list中的玩具对象,一个一个取出来,玩完!这个小孩可真够厉害的,呵呵。可以想象为,该类的工作就是不停地向list中取出玩具对象。OK,再来编写方法,如下
主方法
public class MainTest {
/**
* @param args
*/
public static void main(String[] args) {
Factory f = new Factory();
f.start();
Kid k = new Kid();
k.start();
}
}
最后是Tools类,他里面有个静态的List对象:
Tools类
public class Tools {
public static List<Toy>lT = new ArrayList<Toy>(10000);
}
这样,我们运行一下主方法,看看我们这位“天才”玩完100000个玩具,得花销多少的时间。
好,31毫秒。
这是我们的第一种解决方法,下面再来看第二种解决方法:
其实我们在Factory类和Kid类中都进行了同步处理,这样一来,浪费了很多时间,到底是不是这样的呢?我们可不可以直接用一个不用处理线程同步的容器来放Toy类对象呢?这样以来是不是就可以节省很多开销了?这个想法是有道理的,但是,事实是不是这样的呢?马上实践!
代码就不具体贴出来了,只是我们在Tools类中用到的是一个如下的对象
Public static LinkedBlockingQueue<Toy> lT= new LinkedBlockingQueue<Toy>(1000);
对,阻塞队列,这样我们就只管往里面取,从里面拿了,不用自己考虑同步问题,Factory类和Kid类中也不同特意去加关键字进行同步了。
那么这种方案的结果是多少呢?同样是100000个玩具,看结果
哦哦,变成16毫秒了,着实提高了不少效果呢。看来,在处理同步的时候挤时间,是有发展空间的,呵呵。
等等,有人要发话了,你在这磨叽了半天,还是没有说什么是双缓冲啊,对!有了前面的两种方案,我们再来看看“双缓冲队列”。
所谓双缓冲队列,自然起码要有两个队列吧,呵呵,在这个例子中,我们可以设计两个List来存放工厂生产出来的玩具对象。
下面分析一下:
两个List,一个用来存,一个用来取。有点迷糊?就是有一个listP从工厂那里得到玩具对象,另外一个listT就专门把它得到的玩具对象送去给Kid类处理。当listT变成空的了以后,再将listP中在这段时间内取到的所有玩具对象放到listT中,好,这完了之后,他们两个就又各自干各自的去了:listP再去取,listT再去送。这样是不是就减少了很多次的线程同步呢?至少,在它们交换之前,listP是完全被工厂类线程占有,listT是完全被Kid类线程占有的,不用处理同步。只有在listT放完了,没得给了,再去跟ListP换过来,这个时候就要处理同步了。
跟实际联系一下,有两个搬运工A,B,A在工厂,专门从工厂取玩具;B在小孩子身边,专门送玩具给小孩玩。当B身上没有玩具了,自然要回A那里,把A身上的玩具全部拿过来,再来送给小孩玩。在A还有玩具的时候,A和B是在各自的线程里被处理的,即A在拿,B在给。不用担心同步问题。
这样以来,处理同步问题的次数是不是大大减少了呢?没错,就是这样的。那么怎么跟代码结合呢?
我们要设计一个监视线程,监视listP是不是空了,要是空了,把它同步起来,把listT也同步起来,让他们交换。完了就各自干各自的了。
我们来看看这个监视类:
public class DoubleBufferList {
private List<Object> lP;
private List<Object> lT;
private int gap;
/**
* 构造方法
*
* @param lP
* 用来存放对象的队列
* @param lT
* 用来取对象的队列
* @param gap
* 交换的间隔
*/
public DoubleBufferList(List lP, List lT, int gap) {
this.lP = lP;
this.lT = lT;
this.gap = gap;
}
public void check() {
Runnable runner = new Runnable() {
public void run() {
while (true) {
if (lT.size() == 0) {
synchronized (lT) {
synchronized (lP) {
lT.addAll(lP);
}
lP.clear();
}
}
try {
Thread.sleep(gap);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runner);
thread.start();
}
}
这个类中的线程方法就是用来交换的,目的就是为了减少处理同步的次数。这种方案中,Facotry类和Kid类跟前面单个List的情况差不多,就不再给出了。只是有一点要注意,Facory类中将玩具对象是放到了lP中,而Kid类中,也只是从lT中去取对象。看看Tools类中,多了一个变量:
Tools类,声明两个队列
public static List<Toy>lT = new ArrayList<Toy>(10000);
public static List<Toy>lP = new ArrayList<Toy>(10000);
同样是让我们的“天才”玩完100000个玩具,来看看运行需要的时间:
哈哈,似乎跟我们上面的第二种方案,单阻塞队列,没有太大的差异。怎么解释呢?
不用着急,来,我将额定的玩具量后多加个“0”,让他玩完1000000个!改一下单阻塞队列方案的输出结果,给他们一个标记。再来看看结果:
效果出来了吧,我们再加大量,让他们同时处理10000000个玩具对象:
充分说明,使用双缓冲队列,比单缓冲阻塞队列的效果要好,更别说单缓冲队列了。
总结:
从上面的分析,我们可以得知,在处理线程同步的时候,是要花费我们的时间的,虽然在有些时候,这样的花费是我们可以接受的,但是在很多情况下,如果我们能注意到这样的浪费,并且及时地完善我们的程序,这样可以更大限度地提高我们程序的运行效率。尤其是在大的程序里面,这样的效果体现得更明显。而往往越大的系统,对性能的要求也就越高。
希望这次对“双缓冲队列”的分析和设计,能对各位以后的开发带来一些帮助。
- 大小: 3.7 KB
- 大小: 5.1 KB
- 大小: 5.4 KB
- 大小: 6.3 KB
- 大小: 5.3 KB
- 大小: 4.8 KB
- 大小: 5.2 KB
分享到:
相关推荐
双缓冲队列的设计与实现,多线程,c语言实现,用于提高程序运行效率
"C#版服务端双缓冲队列"就是一种为了解决此类问题而设计的数据结构。这个概念基于“空间换时间”的策略,即通过额外的空间来减少处理时间,以提升程序的执行效率。本文将详细讲解双缓冲队列的概念、实现原理以及它在...
在IT行业中,服务器端双缓冲队列是一种优化数据处理效率的设计模式,特别是在高并发和实时性要求较高的场景下。双缓冲队列(Double Buffering Queue)借鉴了图形处理中的双缓冲技术,通过两个独立的数据缓冲区来提高...
C++数据结构与算法之双缓存队列实现方法详解 本文主要介绍了C++数据结构与算法之双缓存队列实现方法,结合实例形式分析了双缓存队列的原理、实现方法与相关注意事项。 知识点一:双缓存队列的定义 双缓存队列是一...
双缓冲技术是计算机图形学中一种优化显示更新的技术,尤其在游戏开发和用户界面设计中广泛应用。它旨在解决屏幕闪烁和图像撕裂问题,提供更流畅的视觉体验。本教程结合源码,将深入讲解双缓冲的工作原理及其应用。 ...
### VC双缓冲实现方法详解 在图形图像处理领域,尤其是基于Windows编程的环境中,双缓冲技术是一项关键且广泛采用的技术,旨在解决复杂图形处理时窗口重绘造成的闪烁问题。闪烁通常发生在窗口重绘频率较高的场景下...
双缓冲队列使用 lambda 实现双缓冲队列和范围保护
### 环形队列缓冲区的关键知识点 #### 一、环形缓冲区的基本概念与应用 环形缓冲区(Circular Buffer),又称循环队列,是一种高效的数据结构,在嵌入式系统、网络通信、多媒体处理等领域有着广泛的应用。它通过在...
STM32F4系列芯片的DMA双缓冲模式是一种高效的数据传输机制,尤其在处理大量数据时,能够显著减轻CPU的负担并提高系统性能。在STM32家族中,STM32F2、STM32F4和STM32F7等系列支持这种模式。STM32的DMA分为通用DMA和...
为了解决这些问题,我们使用了多种技术和方法,例如使用DirectShow技术来实现视频的解码播放,使用互斥锁技术来保护服务器端的视频数据,使用双缓冲队列技术来实现客户端的视频数据接收和服务器端的视频数据发送。...
为了解决相邻处理阶段吞吐率的差异问题,在流水线相邻节点间加入双缓冲队列来存储节点处理的中间结果,进一步提高EVI提取算法各步骤并行度。实验结果表明,提出的EVI快速提取算法的提取效率要高于传统的EVI多线程...
一个C++实现的线程安全的双缓冲实例 scenario: a multithreaded business system writes data to memory, while a synchronous thread reads data and synchronizes to redis. 场景:多线程的业务系统不断写入数据到...
无锁缓冲队列是一种在多线程环境下高效且并发友好的数据结构,它在计算机科学和编程领域,尤其是在并发编程和高性能计算中占有重要地位。无锁缓冲队列(也称为原子队列或lock-free queue)的设计目标是通过避免锁的...
在本文中,我们将深入探讨如何使用Microsoft Foundation Class (MFC) 库来实现一个具有最短路径搜索功能的用户界面,同时应用双缓冲技术来消除屏幕闪烁,提供更流畅的用户体验。首先,让我们理解最短路径算法、双...
本文实例讲述了C#手工双缓冲技术。分享给大家供大家参考。具体如下: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System....
2. 双缓冲队列:双缓冲队列是一种提高I/O操作性能的技术,能够实现数据的平滑传输。在分布式缓存系统中,客户端采用双缓冲队列技术,可以有效缓解突发业务量对系统造成的冲击,保证数据处理的连贯性。 3. 连接池组...
双缓冲区音频播放是一种常见的音频播放策略,它能确保音频播放的连续性,避免因为数据处理或加载延迟导致的声音断断续续。在双缓冲区机制中,有两个缓冲区交替工作:一个缓冲区正在被硬件播放,而另一个则在后台填充...
在网络通信设计中,双缓冲队列是一种常见的优化手段,它能提高数据传输的效率和稳定性。在本文提出的通信模型中,客户端和服务器均采用了基于流Socket的TCP连接,确保数据的可靠传输和顺序性。客户端首先通过指定...
4. **多线程双缓冲队列**: 在播放过程中,为了确保流畅性,通常会采用多线程技术。一个线程负责读取和解码音视频数据,另一个线程则负责将解码后的数据送入播放队列。双缓冲机制可以避免在更新UI时出现闪烁或卡顿...