Producteur/Consommateur est un design pattern qui permet de faire communiquer :
La difficulté est de pouvoir:
Lorsque la file est pleine, les producteurs restent bloqués en attentant que la file se vide.
La file bloquante est le seul mécanisme de synchronisation utilisé ici !
La file des producteurs/consommateurs est déjà
implantée en Java. Il existe plusieurs implémentations de l'interface BlockingQueue
.
ArrayBlockingQueue
:ArrayDeque
).
LinkedBlockingQueue
:SynchronousQueue
:Extrait de la doc Java:
Throws exception | Special value | Blocks | Times out | |
Insert | add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
Remove | remove() |
poll() |
take() |
poll(time, unit) |
Examine | element() |
peek() |
- | - |
Dans le contexte du pattern producteur/consommateur, on n'utilise jamais les méthodes levant une exception ; et très rarement celles renvoyant une valeur spéciale.
BlockingQueue<String> queue = new ... // quelle implémentation ? for (var i = 0; i < 3; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(100); queue.put(Thread.currentThread().getName()); } catch (InterruptedException e) { return; } } }); } for (var i = 0; i < 2; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(500); System.out.println("next " + queue.take()); } catch (InterruptedException e) { return; } } }); }
BlockingQueue<String> queue = new ... for (var i = 0; i < 3; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(100); queue.put(Thread.currentThread().getName()); } catch (InterruptedException e) { return; } } }); } for (var i = 0; i < 2; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(500); if ( !queue.isEmpty() ){ System.out.println("next : " + queue.remove()); // idem avec queue.poll() } } catch (InterruptedException e) { return; } } }); }
var queue = new LinkedBlockingQueue<String>(); for (var i = 0; i < 3; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(100); queue.put(Thread.currentThread().getName()); } catch (InterruptedException e) { return; } } }); } for (var i = 0; i < 2; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(500); System.out.println("next : " + queue.take()); } catch (InterruptedException e) { return; } } }); }
La file va grandir jusqu'à faire une OutOfMemoryError
.
var queue = new ArrayBlockingQueue<String>(10); for (var i = 0; i < 3; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(100); queue.put(Thread.currentThread().getName()); } catch (InterruptedException e) { return; } } }); } for (var i = 0; i < 2; i++) { Thread.ofPlatform().start(() -> { for(;;) { try { Thread.sleep(500); System.out.println("next : " + queue.take()); } catch (InterruptedException e) { return; } } }); }
Plus de possiblité de OutOfMemoryError
.