public void server() {
for (;;) {
Request
r = waitForRequestFromBrowser();
handler.handleRequest(r);
}
}
}
Webservern ligger i en evig loop där den väntar på anrop
från webläsare. När det kommer ett anrop skickas det vidare
till ett objekt av klassen RequestHandler som avkodar begäran
från browsern, läser in den efterfrågade HTML-filen och
skickar tillbaks den över nätet. Det asynkrona meddelandet är
anropet handler.handleRequest(). Sändaren är ett objekt
av klassen WebServer och mottagaren är objektet handler.
Ovanstående konstruktion av webservern är urusel eftersom den inte kan svara på ett anrop innan den är helt klar med föregående anrop. Svarstiden kommer att bli på tok för lång, en användare som surfar till en sajt vilken hanteras av ovanstående webserver kommer förmodligen att anta att sajten är nere eftersom det tar så lång tid att få svar. Dessutom kommer sannolikt webserverns inbuffert att bli full av alla väntande anrop, vilket leder till att nya anrop tappas. Detta är ett praktexempel på när det lönar sig att göra ett program flertrådat.
public void server() {
for (;;) {
final
Request r = waitForRequestFromBrowser();
new
Thread(new Runnable() {
public void run() {
new RequestHandler().handleRequest(r);
}
}).start();
}
}
}
Nu kommer en ny tråd att skapas och startas på varje varv i for-loopen.
Allt den nya tråden gör är att exekvera det asynkrona meddelandet
handleRequest(). Varje gång ett meddelande skickas skapas
alltså en ny tråd som kör meddelandet och sedan dör.
Detta är en mycket bättre lösning än den föregående,
men den är knappast den bästa. Problemet är dels att det tar
ganska lång tid att skapa en ny tråd och starta den. Det innebär
att svarstiden blir något längre än om tråden som skulle
exekvera meddelandet redan fanns. Dessutom kan denna strategi leda till att
för många trådar skapas om det kommer många anrop
i snabb följd. Detta kan till exempel orsaka att webservern krashar
på grund av att minnet tar slut.
public void server() {
for (;;) {
final
Request r = waitForRequestFromBrowser();
workers.execute(new
Runnable() {
public void run() {
new RequestHandler.handleRequest(r);
}
});
}
}
}
class WorkerPool {
protected final Channel workQueue = new Channel();
public void execute(Runnable r) {
workQueue.put(r);
}
public WorkerPool(int nWorkers) {
for(int i=0; i<nWorkers;
i++) {
activateWorker();
}
}
protected void activateWorker() {
Runnable runLoop = new Runnable()
{
public
void run() {
for (;;) {
Runnable r = (Runnable)workQueue.take();
r.run();
}
}
};
new Thread(runLoop).start();
}
}
Klassen WorkPool innehåller ett antal trådar som utför
de meddelanden som skickas till dem via metoden execute(). Klassen
Channel kan vara en vanlig buffer i stil med den BufferArray vi tidigare använt.
Det finns ett flertal frågeställningar att fundera kring när en trådpool ska implementeras. Här är några av dem: