J'ai une classe qui prend des objets d'un BlockingQueue
et les traite en appelant take()
dans une boucle continue. À un moment donné, je sais qu'aucun objet ne sera ajouté à la file d'attente. Comment puis-je interrompre la méthode take()
afin qu'elle cesse de bloquer?
Voici la classe qui traite les objets:
public class MyObjHandler implements Runnable {
private final BlockingQueue<MyObj> queue;
public class MyObjHandler(BlockingQueue queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
MyObj obj = queue.take();
// process obj here
// ...
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Et voici la méthode qui utilise cette classe pour traiter les objets:
public void testHandler() {
BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100);
MyObjectHandler handler = new MyObjectHandler(queue);
new Thread(handler).start();
// get objects for handler to process
for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); ) {
queue.put(i.next());
}
// what code should go here to tell the handler
// to stop waiting for more objects?
}
Si l'interruption du thread n'est pas une option, une autre consiste à placer un objet "marqueur" ou "commande" dans la file d'attente qui serait reconnu comme tel par MyObjHandler et sortirait de la boucle.
BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100);
MyObjectHandler handler = new MyObjectHandler(queue);
Thread thread = new Thread(handler);
thread.start();
for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); ) {
queue.put(i.next());
}
thread.interrupt();
Toutefois, si vous procédez ainsi, le thread peut être interrompu tant qu'il reste des éléments dans la file d'attente, en attente de traitement. Vous voudrez peut-être envisager d'utiliser poll
au lieu de take
, ce qui permettra au thread de traitement de se terminer et de se terminer lorsqu'il aura attendu un certain temps sans nouvelle entrée.
Très tard mais j'espère que cela aide les autres aussi J'ai rencontré le même problème et j'ai utilisé l'approche poll
suggérée par erickson ci-dessus avec quelques modifications mineures,
class MyObjHandler implements Runnable
{
private final BlockingQueue<MyObj> queue;
public volatile boolean Finished; //VOLATILE GUARANTEES UPDATED VALUE VISIBLE TO ALL
public MyObjHandler(BlockingQueue queue)
{
this.queue = queue;
Finished = false;
}
@Override
public void run()
{
while (true)
{
try
{
MyObj obj = queue.poll(100, TimeUnit.MILLISECONDS);
if(obj!= null)//Checking if job is to be processed then processing it first and then checking for return
{
// process obj here
// ...
}
if(Finished && queue.isEmpty())
return;
}
catch (InterruptedException e)
{
return;
}
}
}
}
public void testHandler()
{
BlockingQueue<MyObj> queue = new ArrayBlockingQueue<MyObj>(100);
MyObjHandler handler = new MyObjHandler(queue);
new Thread(handler).start();
// get objects for handler to process
for (Iterator<MyObj> i = getMyObjIterator(); i.hasNext(); )
{
queue.put(i.next());
}
// what code should go here to tell the handler to stop waiting for more objects?
handler.Finished = true; //THIS TELLS HIM
//If you need you can wait for the termination otherwise remove join
myThread.join();
}
Cela a résolu les deux problèmes
BlockingQueue
pour qu'il sache qu'il ne faut plus attendre les élémentsInterrompez le fil:
thread.interrupt()
Ou n'interrompez pas, c'est méchant.
public class MyQueue<T> extends ArrayBlockingQueue<T> {
private static final long serialVersionUID = 1L;
private boolean done = false;
public ParserQueue(int capacity) { super(capacity); }
public void done() { done = true; }
public boolean isDone() { return done; }
/**
* May return null if producer ends the production after consumer
* has entered the element-await state.
*/
public T take() throws InterruptedException {
T el;
while ((el = super.poll()) == null && !done) {
synchronized (this) {
wait();
}
}
return el;
}
}
queue.notify()
, s'il se termine, appelez queue.done()