Gruppen 5 und 7
Samuel Teuber
propa@teuber.dev
https://teuber.dev/propa
Obere Grenze für Beschleunigung auf 4-Kern-Prozessor?
Reminder: Buddy-System, Immer eine*r der beiden Buddies muss anwesend sein
Sehr grobgranulare Synchronisation:
public synchronized boolean tryTakeVacation(String name, Month month) {
// ....
if (employee.isOnVacation(month)) return true;
if (buddy.isOnVacation(month)) return false;
employee.setVacationState(month, true);
return true;
}
Problem?
Nur ein einziger Aufruf im ganzen Unternehmen gleichzeitig möglich!
Synchronized weglassen?
Nein, da sonst Race Condition!
Reminder: Buddy-System, Immer eine*r der beiden Buddies muss anwesend sein
synchronized(employee) {
if (employee.isOnVacation(month) return true;
synchronized (buddy) {
if (buddy.isOnVacation(month) return false;
employee.setVacationState(month, true);
}
}
Problem?
Deadlock! Lösung?
Reminder: Buddy-System, Immer eine*r der beiden Buddies muss anwesend sein
EmployeeVacationRecord outerMonitor;
EmployeeVacationRecord innerMonitor;
if (employee.getName().compareTo(buddy.getName()) < 0) {
outerMonitor = employee;
innerMonitor = buddy;
} else {
outerMonitor = buddy;
innerMonitor = employee;
}
Optimale Lösung?
Monitorobjekt für die Buddies:
employee.getMonitorObject()
public interface IBarrier {
void await() throws InterruptedException;
void freeAll();
}
Reentrant?
public class Barrier implements IBarrier {
private final int maxThreads;
private int threadsInCurrentWaitingPhase = 0;
private int currentWaitingPhase = 1;
public Barrier(int maxThreads) {
this.maxThreads = maxThreads;
}
@Override
public synchronized void await() throws InterruptedException {
final int myPhase = currentWaitingPhase;
++threadsInCurrentWaitingPhase;
if (threadsInCurrentWaitingPhase == maxThreads) {
freeAll();
}
while (currentWaitingPhase == myPhase) {
this.wait();
}
}
@Override
public synchronized void freeAll() {
++currentWaitingPhase;
threadsInCurrentWaitingPhase = 0;
this.notifyAll();
}
}
public class HappensBefore {
public static boolean ping = false;
public static final int maxRuns = 100;
public static void main(String[] args) {
Thread pingThread = new Thread(()-> {
for(int i = 0; i < maxRuns; i++) {
while(ping) {}
ping = true;
System.out.println("Ping - Round " + i);
}
});
Thread pongThread = new Thread(()-> {
for(int i = 0; i < maxRuns; i++) {
while(!ping) {}
ping = false;
System.out.println("Pong - Round " + i);
}
});
pingThread.start();
pongThread.start();
}
}
Warum bleibt das Programm manchmal hängen?
private static class State {
private int currentNumber = 1;
private int result = 0;
private final int target;
State(int target) {
this.target = target;
}
synchronized int nextNumber() {
return currentNumber < target ? ++currentNumber : -1;
}
synchronized void addToResult(Integer count) {
this.result += count;
}
int getResult() {
return result;
}
}
|public static int countPrimes(final int until, final int threads) {
| final State state = new State(until);
| Set<CompletableFuture<Void>> futures = new HashSet<>();
| for (int i = 0; i < threads; ++i) {
| futures.add(CompletableFuture.supplyAsync(() -> {
| int currentNumber = 0;
| int partialCount = 0;
| while (currentNumber != -1) {
| if (PrimeTester.isPrime(currentNumber))
| ++partialCount;
| currentNumber = state.nextNumber();
| }
| return partialCount;
| }).thenAccept(state::addToResult));
| }
| for (CompletableFuture<Void> future : futures) {
| try {
| future.get();
| } catch (InterruptedException | ExecutionException e) {
| e.printStackTrace();
| return -1;
| }
| }
| return state.getResult();
|}
Warum?
public static int binarySearch(int[] a, int key) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
int midVal = a[mid];
if (midVal < key)
low = mid + 1
else if (midVal > key)
high = mid - 1;
else
return mid;
}
return -(low + 1);
}
Syntax: Kommentare mit @ beginnend
boolean
==>
Implikation<==>
Äquivalenz<=!=>
XOR\old(expr)
Wert von expr vor Methode\result
Rückgabewertpublic int x;
public int y;
public int z;
/*@ requires y!=0
@ ensures \old(y)+1 == y
@ ensures x == \old(y)
@*/
public void doStuff() {
x = y++;
z = 10/x;
}
(\forall C x; B; R)
(\exists C x; B; R)
(\sum C x; B; R)
(\product C x; B; R)
(\max C x; B; R)
(\min C x; B; R)
(\num_of C x; B; R)
Diesmal R
numerischer Ausdruck
/*@
@ requires arr != null;
@ ensures (\forall int i; 0<=i && i<(arr.length-1); arr[i]<=arr[i+1])
@ ensures arr.length == \old(arr.length)
@ ensures (\forall int k; 0<=k && k<\old(arr.length);
@ (\numof int i; 0<=i && i<arr.length;
@ arr[i] == \old(arr[k])) ==
@ (\numof int j; 0<=j && j<arr.length;
@ \old(arr[j]) == \old(arr[k])))
@*/
void sort(int[] arr){/*...*/}
/*@ nullable @*/ List<Integer> list
assignable
für veränderbare Parameterassignable \nothing
class IntegerSet {
byte[] a; /* The arraya is sorted*
/*@
@ invariant (\forall int i;
@ 0 <= i && i < a.length-1;
@ a[i]< a[i+1]);
@*/
}
//...
public int doSth() {
this.i+=10;
return this.j;
}
/*@ requires a > 0
@ ensures doSth() = \old(a)
@*/
public void doSthElse(int a) {/*...*/}
//...
Geht das?
Nein! Denn doSth
hat Seiteneffekte.
Jede Funktion, die aus JML aufgerufen wird darf keine Seiteneffekte haben!
public /*@ pure @*/ int getBalance() {/*..*/}
public static int binarySearch(int[] a, int key) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
int midVal = a[mid];
if (midVal < key)
low = mid + 1
else if (midVal > key)
high = mid - 1;
else
return mid;
}
return -(low + 1);
}
Barbara Liskov
Subtype Requirement:
Let $\Phi(x)$ be a property provable about objects $x$ of type $T$.
Then $\Phi(y)$ should be true for objects $y$ of type $S$ where $S$ is a subtype of $T$.
_
/-/ `\
>\ |@@ |-_______
\ \__/ | `\
\____/"\__/ | h
| \ | h
| |\______\ |
|_| |_|
Aktoren (Threads oder Prozesse) kommunizieren per Nachrichten
Wie MPI, aber:
Everything is an actor
Verhalten auf Nachricht:
public class HelloWorld extends AbstractActor {
@Override
public void preStart() {
// create the greeter actor
final ActorRef greeter =
getContext().actorOf(Props.create(HelloWorldActor.class), "greeter");
// tell it to perform the greeting
greeter.tell("printHello", getSelf());
}
@Override
public void createReceive() {
return receiveBuilder()
.match(String.class,
message -> message.equals("shutdown"),
this::shutdown)
.matchAny(message -> unhandled(message))
.build();
}
private void shutdown(String message) {
getContext().stop(getSelf());
}
}
public class HelloWorldActor extends AbstractActor{
@Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class,
message -> message.equals("printHello"),
this::handleStringMessage)
.matchAny(message -> unhandled(message))
.build();
}
private void handleStringMessage(String message) {
System.out.println("Hello World!");
getSender().tell("shutdown",getSelf());
}
}