java生产者 - 消费者优化

时间:2011-04-07 06:47:30

标签: java multithreading synchronization

我正在编写一个绘制某些形状的应用程序。

在绘制每个形状之前,需要进行一些计算。这就是为什么我有另一个线程来绘制已处理的项目。

为了在两个线程之间进行同步,我使用了一个信号量。在抽屉试图从队列中提取物品之前,它将获得许可证,并且每次物品入队时它将释放许可证。除此之外,enqueuedequeue方法是同步的。如果在计算线程完成时许可证数量不为零,计算线程将等待抽屉完成。

修改 计算线程中的同步块是为了防止计算线程在绘制线程完成之前继续。如果确实如此,那就会搞砸了。

分析后,释放许可需要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);
            }
        }

2 个答案:

答案 0 :(得分:3)

使用您当前的实现,似乎您正在强制线程以锁步方式运行。如果您的队列是线程安全的,例如ArrayBlockingQueue,则不需要信号量提供的显式同步。一旦计算线程完成更新项目,它就可以将它放入队列中。计算线程可以在绘图线程获取它们的同时将元素放入队列中。原则上,您可以让多个计算线程并行执行计算。绘图线程使用poll()从队列中获取项目,如果队列为空,它将阻塞该线程。

这将使代码更清晰,并更多地依赖已建立的类。如果同步开销仍然过高,则可以批量进行计算,并将该批计算作为单个项添加到队列中。然后队列类型将(例如)List<QueuedDraw>

编辑:如果计算与阻塞成本相比相对较快,则等待的成本可能很大(如您所见),因此阻塞队列在这里并不理想。而是使用具有“无等待”算法的队列,例如ConcurrentLinkedQueue。这种类型的队列不会使用调度程序阻塞线程,而是使用原子操作来保护对共享状态的访问。

请记住,您的绘图线程只能以您计算的速度绘制。如果项目花费的时间比计算时间长得多,那么花费大量时间等待项目可用是可能的。并行计算将有助于绘图线程做更多工作。

答案 1 :(得分:0)

使用BlockingQueue.takeBlockingQueus.put

这些或acquire将显示大部分时间花费的原因(很可能)是这些方法阻止,当没有任何内容可用或队列已满(如果它可能已满)时。