我正在编写一个绘制某些形状的应用程序。
在绘制每个形状之前,需要进行一些计算。这就是为什么我有另一个线程来绘制已处理的项目。
为了在两个线程之间进行同步,我使用了一个信号量。在抽屉试图从队列中提取物品之前,它将获得许可证,并且每次物品入队时它将释放许可证。除此之外,enqueue
和dequeue
方法是同步的。如果在计算线程完成时许可证数量不为零,计算线程将等待抽屉完成。
修改 计算线程中的同步块是为了防止计算线程在绘制线程完成之前继续。如果确实如此,那就会搞砸了。
分析后,释放许可需要8.3%
计算线程的运行时间。这是可以接受的。问题在于绘图线程。获取方法占用了运行时间的63%。
除此之外,我尝试使用阻塞队列,但情况更糟。
代码:的
绘图主题:
private void drawNext(){
QueuedDraw item;
if(drawingStatus.availablePermits()==0)
synchronized(drawingStatus){
if(drawingStatus.availablePermits()==0)
drawingStatus.notify();
}
try {
drawingStatus.acquire();
} catch (InterruptedException ex) {
Logger.getLogger(PolygonDrawer.class.getName())
.log(Level.SEVERE, null, ex);
}
item = queue.dequeue();
fillPoly(item.points, item.color, item.vertices);
}
public final void fillSquare(Point[] points, byte[] color){
queue.enqueue(new QueuedDraw(points, color, SQUARE_VERTICES));
drawingStatus.release();
}
public final void fillTriangle(Point[] points, byte[] color){
queue.enqueue(new QueuedDraw(points, color, TRIANGLE_VERTICES));
drawingStatus.release();
}
@Override
public void run() {
while(true){
drawNext();
}
}
计算主题
注意:在此块之前是触发fillSquare / fillTriangle
的所有方法调用if(paintStatus.availablePermits()!=0)
synchronized(paintStatus){
try {
if(paintStatus.availablePermits()!=0)
paintStatus.wait();
} catch (InterruptedException ex) {
Logger.getLogger(Painter.class.getName())
.log(Level.SEVERE, null, ex);
}
}
答案 0 :(得分:3)
使用您当前的实现,似乎您正在强制线程以锁步方式运行。如果您的队列是线程安全的,例如ArrayBlockingQueue,则不需要信号量提供的显式同步。一旦计算线程完成更新项目,它就可以将它放入队列中。计算线程可以在绘图线程获取它们的同时将元素放入队列中。原则上,您可以让多个计算线程并行执行计算。绘图线程使用poll()从队列中获取项目,如果队列为空,它将阻塞该线程。
这将使代码更清晰,并更多地依赖已建立的类。如果同步开销仍然过高,则可以批量进行计算,并将该批计算作为单个项添加到队列中。然后队列类型将(例如)List<QueuedDraw>
。
编辑:如果计算与阻塞成本相比相对较快,则等待的成本可能很大(如您所见),因此阻塞队列在这里并不理想。而是使用具有“无等待”算法的队列,例如ConcurrentLinkedQueue。这种类型的队列不会使用调度程序阻塞线程,而是使用原子操作来保护对共享状态的访问。
请记住,您的绘图线程只能以您计算的速度绘制。如果项目花费的时间比计算时间长得多,那么花费大量时间等待项目可用是可能的。并行计算将有助于绘图线程做更多工作。
答案 1 :(得分:0)
使用BlockingQueue.take
和BlockingQueus.put
。
这些或acquire
将显示大部分时间花费的原因(很可能)是这些方法阻止,当没有任何内容可用或队列已满(如果它可能已满)时。