Parallele Programmierung (3IB)
=> Grundlage: Hettel und Tran (2016, Kap. 3 und 4)
volatile)synchronized-Methoden und -Blöcke und “Lock-Objekte” (Monitor)Vector

public class RamTest extends Thread {
private final int i;
public RamTest(int i) {
this.i = i;
}
public void print(int i) {
var a = i * i;
var b = Integer.valueOf(a);
System.out.println(b);
}
@Override
public void run() {
print(this.i);
}
public static void main(String... args) {
new Thread(new RamTest(2)).start();
new Thread(new RamTest(3)).start();
}
}Hauptprogramm:

Hauptprogramm:
new RamTest(2) Aufruf
Hauptprogramm:
new RamTest(2) Objekterzeugung
Hauptprogramm:
new RamTest(2) fertig
Hauptprogramm:
new RamTest(2).start()
Hauptprogramm:
new RamTest(3)
Hauptprogramm:
new RamTest(3).start()
Hauptprogramm:

Hauptprogramm:
run() \(\to\) print(this.i)
Hauptprogramm:
var a = i * i (parallel)
Hauptprogramm:
var b = Integer.valueOf(a) 2x
Fall 1:
t3: temp = 1
t3: temp = 1+3
t3: c.counter = 4
t4: temp = 4
t4: temp = 4+4
t4: c.counter = 8
\(\to\) 8
Fall 1:
t3: temp = 1
t3: temp = 1+3
t3: c.counter = 4
t4: temp = 4
t4: temp = 4+4
t4: c.counter = 8
\(\to\) 8
Fall 2:
t3: temp = 1
t3: temp = 1+3
t4: temp = 1
t3: c.counter = 4
t4: temp = 1+4
t4: c.counter = 5
\(\to\) 5
Fall 1:
t3: temp = 1
t3: temp = 1+3
t3: c.counter = 4
t4: temp = 4
t4: temp = 4+4
t4: c.counter = 8
\(\to\) 8
Fall 2:
t3: temp = 1
t3: temp = 1+3
t4: temp = 1
t3: c.counter = 4
t4: temp = 1+4
t4: c.counter = 5
\(\to\) 5
Fall 3:
t3: temp = 1
t4: temp = 1
t3: temp = 1+3
t4: temp = 1+4
t4: c.counter = 5
t3: c.counter = 4
\(\to\) 4
Möglichkeit unterschiedlicher Traces: Race Condition (auch: Data Race)
kritische Abschnitte (engl. “critical regions”)
Im vorigen Beispiel sind die Methoden set und add kritische Abschnitte.
Sie schließen sich gegenseitig und untereinander aus.
gegenseitiger Ausschluss = “mutual exclusion”, verkürzt “mutex”
volatile gekennzeichnet).
Threads greifen immer auf eine lokale Kopie des Heaps zu!
volatile
synchronized
synchronized-Blocksynchronized-Blocks
Folgende Fälle bilden Memory Barriers, an denen die Cache-lokalen Änderungen zwischen Threads ausgetauscht werden:
volatile-VariablensynchronizedFolgende Fälle bilden Memory Barriers, an denen die Cache-lokalen Änderungen zwischen Threads ausgetauscht werden:
0, false oder null) aller Variablen sorgt für die Synchronisierung mit dem ersten Zugriff.join()-Aufruf zurückkehrt, sieht der Aufrufer alle von dem Thread gemachten Änderungen.isInterrupted() liefert immer den aktuellen Unterbrechungsstatus.pp.02.01-MemoryBarrierBesonderheit: threadlokaler Speicher (TLS)
T, wenn über ThreadLocal<T> erzeugtT Integerpublic class ThreadLocalDemo {
public static class Runner implements Runnable {
public static ThreadLocal<Integer> mem =
new ThreadLocal<>() {
@Override
protected Integer initialValue() {
return Integer.valueOf(1);
}
};
@Override
public void run() {
while (true) {
mem.set(mem.get() + 1);
}
}
}
public static void main(String... args) {
var runnable = new Runner();
new Thread(runnable, "Runner-1").start();
new Thread(runnable, "Runner-2").start();
}
}Man kann einen threadlokalen Zufallszahlengenerator abrufen. Dieser ist nicht threadsicher implementiert und damit effizienter als der “normale” Zufallszahlengenerator java.util.Random, der threadsicher implementiert ist. ThreadLocalRandom aus dem Package java.util.concurrent hat aber dieselbe Signatur wie Random:
pp.02.02-ThreadLocalsynchronized in Methoden-Signatursynchronized-Methoden eines Objekts gemeinsamsynchronized-Block fungiert gleichzeitig als Memory Barrier (wichtig für Getter und Setter)synchronized-Block
thisstatic ist (Klassenmethode), wird die Klasse selber als Monitor verwendetpp.02.03-LockStringBuffer, Vector und HashTable:
synchronizedStringBuilder, ArrayList, LinkedList, HashMap, IdentityHashMap, TreeMapsynchronizedprivate static Vector<Integer> vec = new Vector<>();
public static void main(String... args)
throws InterruptedException {
var remover = new Thread(() -> {
for (var i = 0; i < vec.size(); i++) {
if ((vec.get(i) % 2) == 1) {
vec.remove(i);
}
}
}, "Odd-Remover");
var adder = new Thread(() -> {
for (var i = 0; i < MAX; i++) {
vec.add(i);
}
}, "Adder");
remover.start(); adder.start();
remover.join(); adder.join();public class Deadlock {
private static Object l1 = new Object();
private static Object l2 = new Object();
public static void m1() {
synchronized (l1) {
synchronized (l2) {
/* ... */
}
}
}
public static void m2() {
synchronized (l2) {
synchronized (l1) {
/* ... */
}
}
}
public static void main(String... args) {
var t1 = new Thread(Deadlock::m1); t1.start();
var t2 = new Thread(Deadlock::m2); t2.start();
}
}public class Deadlock {
private static Object l1 = new Object();
private static Object l2 = new Object();
public static void m1() {
synchronized (l1) {
synchronized (l2) {
/* ... */
}
}
}
public static void m2() {
synchronized (l2) {
synchronized (l1) {
/* ... */
}
}
}
public static void main(String... args) {
var t1 = new Thread(Deadlock::m1); t1.start();
var t2 = new Thread(Deadlock::m2); t2.start();
}
}