Programmeren in Java/Multithreading
Multithreading is het uitvoeren van meerdere taken tegelijkertijd, buiten java ook gekend onder de benaming multitasking. Bij computers die slechts één processor hebben, wordt het werk van de verschillende threads in kleine pakketjes afwisselend uitgevoerd. Als er meerdere processoren aanwezig zijn, kan er echt parallel gewerkt worden.
draad extends Thread
bewerkenVoorbeelden zijn bvb. het wegschrijven door de tekstverwerker van een bestand op de achtergrond, terwijl je al verdertypt.
- Uitdrukkingen
- public void run() bevat de uit te voeren code.
- start() creëert de extra thread, en de JVM voert de run() uit.
- sleep(int mills) om de thread een aantal milliseconden te laten pauzeren.
- name
- priority verdeelt de processortijd tussen de verschillende threads. De waarde ligt tussen MIN_PRIORITY (1) en MAX_PRIORITY (10).
- Voorbeeld
Java-code: draad.java
public class draad extends Thread {
private final String naam;
private final int aantal;
private final String s;
public draad(String threadName, String naam, int aantal, int kolom) { // constructor
super(threadName);
this.naam = naam;
this.aantal = aantal;
final StringBuilder buf = new StringBuilder(kolom + 2);
for (int j = 0; j < kolom; j++)
buf.append('\t');
s = buf.append(" x").toString();
}
public void run() {
for (int i = 0; i < aantal; i++) {
System.out.println(naam + i + s);
}
}
}
Java-code: multi.java
public class multi {
public static void main(String[] args) {
draad reeksA = new draad("taakA", "A", 10, 1);
draad reeksB = new draad("taakB", "B", 10, 2);
draad reeksC = new draad("taakC", "C", 10, 3);
reeksA.start();
reeksB.start();
reeksC.start();
}
}
Als we dit laten lopen, zien we dat het werk mooi verdeeld wordt over de verschillende threads:
A0 x B0 x C0 x A1 x B1 x C1 x ... A8 x B8 x C8 x A9 x B9 x C9 x
draad2 implements Runnable
bewerkenEen iets omslachtiger methode behaalt hetzelfde resultaat als hierboven:
- Uitdrukkingen
- public void run() bevat de uit te voeren code.
- start()
- .currentThread().getName()
- Voorbeeld
Java-code: draad2.java
public class draad2 implements Runnable{
private final String naam;
private final int aantal;
private final String s;
public draad2(String naam, int aantal, int kolom) {
this.naam = naam;
this.aantal = aantal;
final StringBuilder buf = new StringBuilder(kolom + 2);
for (int j = 0; j < kolom; j++)
buf.append('\t');
s = buf.append(" x").toString();
}
public void run() {
for (int i = 0; i < aantal; i++) {
System.out.println(naam + i + s);
}
}
}
Java-code: multi2.java
public class multi2 {
public static void main(String[] args) {
draad2 reeksA = new draad2("A", 10, 1);
draad2 reeksB = new draad2("B", 10, 2);
draad2 reeksC = new draad2("C", 10, 3);
Thread t1 = new Thread(reeksA, "taakA");
Thread t2 = new Thread(reeksB, "taakB");
Thread t3 = new Thread(reeksC, "taakC");
t1.start();
t2.start();
t3.start();
}
}
Synchronisatie
bewerkenTegelijk lopende verrichtingen kunnen elkaar storen. Daarom kan je objecten blokkeren, zodat ze slechts door één thread tegelijkertijd kunnen gebruikt worden. Als echter de ene thread wacht op het vrijkomen van een object dat door een andere thread geblokkeerd wordt omdat het eerst het geblokkeerde object van de eerste thread nodig heeft, ontstaat een zgn. deadlock. Dit treedt ook soms op bij databanken, en dan zal een rollback uitgevoerd worden na een bepaalde deadtime.
- Uitdrukkingen
In Java kan je dergelijke problemen best voorkomen:
- wait() zal gedurende een bepaalde periode het object in de wacht zetten
- notifyAll() haalt alle threads uit de wacht, zodat ze eventueel het object dat net vrijgegeven werd kunnen gebruiken
- synchronised zorgt dat hiermee gemarkeerde methods niet tegelijkertijd worden uitgevoerd
- Voorbeeld
Onderstaande Buffer wordt gevuld door de Producent en geleegd door de Consument, die beiden threads zijn die door het hoofdprogramma Synchro aangestuurd worden:
Java-code: Buffer.java
public class Buffer {
private String inhoud;
private boolean vullen = true; //Schakelaar: Starten met opvullen
public synchronized String haalOp() {
try {
while (vullen) //Wachten op opvulling voor er opgehaald wordt
wait();
} catch (Exception e) {}
vullen = true;
notifyAll();
return inhoud;
}
public synchronized void vulOp(String inhoud) {
try {
while (!vullen) //Wachten op ophaling voor er opnieuw opgevuld wordt
wait();
} catch (Exception e) {}
this.inhoud = inhoud;
vullen = false;
notifyAll();
}
}
Java-code: Producent.java
public class Producent implements Runnable {
private String[] tekst;
private Buffer buf;
public Producent (String[] tekst, Buffer buf) {
this.tekst = tekst;
this.buf = buf;
}
public void run() {
String lijn;
int r = 0;
while (r < tekst.length)
buf.vulOp(lijn = tekst[r++]);
buf.vulOp(null);
}
}
Java-code: Consument.java
public class Consument implements Runnable {
private Buffer buf;
private int aantalWoorden;
public Consument (Buffer buf) {
this.buf = buf;
aantalWoorden = 0;
}
public void run() {
String lijn;
while ((lijn = buf.haalOp()) != null) {
System.out.println(lijn);
for (int i = 0; i < lijn.length(); i++) { // woorden tellen
if (lijn.charAt(i) == ' ')
aantalWoorden++;
}
System.out.println("\t\tTussentotaal = " + ++aantalWoorden);;
}
System.out.println("Totaal aantal woorden = " + aantalWoorden);
}
}
Java-code: Synchro.java
public class Synchro {
public static void main(String[] args) {
String[] tekst = {"Ode aan de vreugde", "Door Filip Muyzenhardt", "Paramaribo"};
Buffer buf = new Buffer();
Producent pro = new Producent(tekst, buf);
Consument con = new Consument(buf);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
Client- en Serverprocessen
bewerkenComputers op een netwerk, als het internet, werken ook met processen die op elkaar wachten:
Java-code: TCPClient.java
import java.io.*;
import java.net.*;
class TCPClient {
public static void main(String argv[]) throws Exception {
String sentence;
String modifiedSentence;
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); //Create input stream
Socket clientSocket = new Socket("localhost", 6789); //Create client socket, connect to server
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream()); // Create output stream attached to socket
BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // Create input stream attached to socket
sentence = inFromUser.readLine();
outToServer.writeBytes(sentence + '\n'); // Send line to server
modifiedSentence = inFromServer.readLine(); // Read line from server
System.out.println("FROM SERVER: " + modifiedSentence);
clientSocket.close();
}
}
Onderstaand proces zal na opstarten de requesten van bovenstaand proces beantwoorden.
Java-code: TCPServer.java
import java.io.*;
import java.net.*;
class TCPServer {
public static void main(String argv[]) throws Exception {
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789); // Create welcoming socket at port 6789
while(true) { //loop back and wait for another client connection
Socket connectionSocket = welcomeSocket.accept(); //Wait, on welcoming socket for contact by client
BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream())); //Create input stream, attached to socket
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream()); //Create output stream, attached to socket
clientSentence = inFromClient.readLine(); //Read in line from socket
capitalizedSentence = clientSentence.toUpperCase() + "\n !!!";
outToClient.writeBytes(capitalizedSentence); //Write out line to socket
}
}
}
Termen die met multi threading te maken hebben
bewerkenLivelock: een thread wacht op een andere thread met bv. Thread.join() totdat hij aan het eind is.
Deadlock: twee of meer threads die zo bezig zijn dat, ze niet meer iets kunnen doen, bv.: thread A wacht totdat thread B eindigt en thread B wacht totdat thread A eindigt