Whisper.java
import java.io.*;
import java.util.Random;
import java.text.NumberFormat;
/**
The game of "Whispers", passing a message from thread to thread
with random changes at each stage. An example of piped input/output
streams communicating between threads.
*/
public class Whisper
{
public static void main(String[] argv)
{
int nworkers = 8;
int i;
try {
// Open input file.
File infile = new File("pips.txt");
FileInputStream instream =
new FileInputStream(infile);
// Open output file.
File outfile = new File("pips.out");
FileOutputStream outstream =
new FileOutputStream(outfile);
// Create the worker threads.
Thread[] threads = new Thread[nworkers];
InputStream in = null;
OutputStream out = null;
InputStream outin = null;
for (i=0; i<nworkers; i++) {
if (i == 0)
in = instream;
else
in = outin;
if (i == nworkers-1)
out = outstream;
else {
out = new PipedOutputStream();
outin = new PipedInputStream(
(PipedOutputStream)out);
}
threads[i] = new Mutator(i,0.05,3,in,out);
}
// Start all threads.
for (i=nworkers-1; i>=0; i--)
threads[i].start();
// Monitor thread progress.
NumberFormat nf = NumberFormat.getIntegerInstance();
nf.setGroupingUsed(false); // No commas in format.
while (true) {
boolean any = false;
for (i=0; i<nworkers; i++) {
if (threads[i].isAlive()) {
String ss = nf.format(
((Mutator)threads[i]).charCount());
ss = " " + ss + " ";
System.out.print(ss.substring(ss.length()-8));
any = true; // At least one thread still runs.
}
else
System.out.print(" done ");
}
System.out.println();
if (!any) // No more threads running.
break;
Thread.sleep(500);
}
}
catch(IOException e_io) {
System.out.println("IO exception in main: " + e_io);
}
catch(InterruptedException e_int) {
System.out.println("Interrupted!");
}
}
}
/**
An instance of this class copies its input to its output, mutating
each character with probability <code>mutationRate</code> and
pausing no more than <code>maxSleep</code> milliseconds between
characters.
*/
class Mutator extends Thread
{
public Mutator(int id,double mutationRate,int maxSleep,
InputStream in,OutputStream out)
{
this.id = id;
this.mutationRate = mutationRate; // Mutation rate.
this.maxSleep = maxSleep;
this.in = in;
this.out = out;
this.rand = new Random();
this.count = 0;
}
public void run()
{
try {
while (true) {
int c = in.read();
if (c == -1)
break;
out.write((int)mutate((char)c,mutationRate,rand));
count++;
Thread.sleep(rand.nextInt(maxSleep));
}
in.close();
out.close();
}
catch(InterruptedException e_int) {
System.out.println("Int(" + id + ": " + e_int);
}
catch(IOException e_io) {
System.out.println("IO(" + id + "): " + e_io);
}
}
/**
How many characters has this thread processed.
*/
synchronized public int charCount()
{
return count;
}
/**
Mutate character '<code>c</code>' with probability
'<code>mrate</code>' using the supplied randome number generator.
*/
private char mutate(char c,double mrate,Random rand)
{
String replacements =
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789";
if (Character.isLetterOrDigit(c))
if (rand.nextDouble() < mrate) {
int n = rand.nextInt(replacements.length());
c = replacements.charAt(n);
}
return c;
}
private int id = 0;
private double mutationRate = 0.0;
private int maxSleep = 0;
private InputStream in;
private OutputStream out;
private Random rand;
private int count;
}