我遇到的问题是:在打印服务器的多线程模拟中,我需要做一些事情:
// in client
if (printer.isAvailable() && printer.isFair(this)) // is fair checks if its fair to allow client to print now
printer.requestToPrint(something)
else
printer.requestToPrintNext() // something like go into queue
问题是:如果requestToPrint
或printer.isAvailable() == false
(即使在函数内),也不应调用printer.isFair(client) == false
。我怎样才能确保这一点?简单的方法可能是将所有内容都包裹在synchronized (printer)
中?但这意味着我将只有一个客户试图在任何时间打印,requestToPrintNext
永远不会被调用?我该如何解决这个问题?
顺便说一句,所有这些功能都是在Interface
中定义的,因此我认为我需要使用所有这些...
答案 0 :(得分:2)
处理打印机的常用方法是在队列中组织打印作业,具有单一界面功能,如果打印机空闲则立即开始打印,否则将作业放入队列。如果提供的接口具有此功能,请使用它。如果没有,请创建自己的。如果打印是同步的,也就是说,直到所有打印都没有返回,请从单独的线程中调用它。
答案 1 :(得分:1)
从初看起来,您的客户似乎有太多的打印机逻辑。我认为客户端的方法更像是:
public void printMe() {
printer.print(this);
}
打印机将处理所有可用的检查和排队和调度:
public synchronized boolean print(Client c, Something something) {
if(isFair(c) && isAvailable()) {
requestToPrint(something);
}
else {
// queue
// Not sure what "requestToPrintNext" does,
// as it has no argument, like "something"
}
}
同步打印方法会阻止其他客户端相互跨越(下一个客户端将被阻塞,直到该方法为当前客户端完成)。正如其他海报所提到的,如果requestToPrint
方法需要很长时间,你可以将其解析出来(只需确保它以某种方式将isAvailable设置为false,以便下一个客户端正确排队)。
上述print
方法假设requestToPrint
方法只是触发打印作业,不阻止整个打印操作。示例实现可能类似于:
public void requestToPrint(Client c) {
setAvailable(false);
Thread job = new Thread(new Runnable() {
@Override
public void run() {
// The actual print work
setAvailable(true);
}
});
job.start();
}
值得注意的是,此实现非常特定于此问题的约束。对于更通用的方法,单个打印队列(由其他答案引用)与单个工作线程为该队列提供服务将更加整洁。一个非常简单的骨架就是:
public void print(Client c) {
synchronized(queue) {
queue.add(c);
}
}
public class PrintWorker implements Runnable {
@Override
public void run() {
while(true) {
Client c = null;
synchronized(queue) {
if(!queue.isEmpty()) {
c = queue.remove(0);
}
}
if(c != null) {
// do print work
}
else {
// maybe add sleep or wait here
// to keep thread from spinning
// too fast and burning CPU
}
}
}
}