Il s'agit d'une technique importante pour connecter entre eux deux processus légers, en connectant un flot de sortie du premier avec un flot d'entrée du second. Cette technique, dite de tube, issue d'une pratique courante entre processus dans le système Unix (appelée en anglais pipe), est appliquée ici à deux agents contrôlés par des threads. Les classes permettant ces connexions entre une sortie et une entrée sont :
Un tube d'entrée doit être connecté à un tube de sortie. Considérons une classe Producteur, dont un constructeur prend en argument un flot de sortie out, et une classe Consommateur, dont un constructeur prend en argument un flot d'entrée in. Comme on doit connecter in et out, ces flots doivent être des tubes, par exemple de caractères :
PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(out);
On peut alors connecter des agents p et c à l'aide de ces flots.
Producteur p = new Producteur(out); Consommateur c = new Consommateur(in);
L'écriture dans out et la lecture dans in seront réalisées par les agents eux-mêmes, chacun ayant son propre thread (l'un des deux pouvant être le thread principal de l'application) :
p.start(); c.start();
Dans l'exemple suivant, le producteur écrit le caractère 'a' sur son flot de sortie toutes les 1000 millisecondes ; le consommateur est continuellement en attente d'un caractère sur son flot d'entrée.
class Producteur implements Runnable { private Writer out; private Thread contrôleur; Producteur(Writer out) { contrôleur = new Thread(this); this.out = out; } void start() { contrôleur.start(); } public void run() { while (true) { try { out.write('a'); Thread.sleep(1000); } catch(InterruptedException e) {} catch(IOException e) {} } } } class Consommateur implements Runnable { private Reader in; private Thread contrôleur; Consommateur(Reader in) { contrôleur = new Thread(this); this.in = in; } void start() { contrôleur.start(); } public void run() { while (true) { try { char c = (char) in.read(); System.out.println(c); } catch(IOException e) {} } } }