Laboraufgaben für Kap. 3

pull/1/head
Sandro Leuchter 2024-10-09 10:48:52 +02:00
parent cdcb0c69fc
commit dff9d7719f
61 changed files with 1673 additions and 631 deletions

View File

@ -1,6 +1,7 @@
package pp; package pp;
public class Sensor extends Thread { public class Sensor extends Thread {
// eigentlich abstract // eigentlich abstract
private final long frequency; private final long frequency;

View File

@ -3,6 +3,7 @@ package pp;
import java.util.Random; import java.util.Random;
public class Thermometer extends Sensor { public class Thermometer extends Sensor {
private final Random rand; private final Random rand;
public Thermometer(long frequency) { public Thermometer(long frequency) {
@ -12,8 +13,7 @@ public class Thermometer extends Sensor {
@Override @Override
protected String reading() { protected String reading() {
return String.format("(%04d freq.): %3d°C", getFrequency(), return String.format("(%04d freq.): %3d°C", getFrequency(), this.rand.nextInt(100));
this.rand.nextInt(100));
} }
public static void main(String... args) { public static void main(String... args) {

View File

@ -1,6 +1,7 @@
package pp; package pp;
public class Sensor extends Thread { public class Sensor extends Thread {
// eigentlich abstract // eigentlich abstract
private final long frequency; private final long frequency;

View File

@ -3,6 +3,7 @@ package pp;
import java.util.Random; import java.util.Random;
public class Thermometer extends Sensor { public class Thermometer extends Sensor {
private final Random rand; private final Random rand;
public Thermometer(long frequency) { public Thermometer(long frequency) {
@ -12,8 +13,7 @@ public class Thermometer extends Sensor {
@Override @Override
protected String reading() { protected String reading() {
return String.format("(%04d freq.): %3d°C", getFrequency(), return String.format("(%04d freq.): %3d°C", getFrequency(), this.rand.nextInt(100));
this.rand.nextInt(100));
} }
public static void main(String... args) { public static void main(String... args) {

View File

@ -1,14 +1,14 @@
package pp; package pp;
class MyWorker implements Runnable { class MyWorker implements Runnable {
private Thread self;
@Override private Thread self;
public void run() {
this.self = Thread.currentThread(); @Override
while (true) { public void run() {
System.out.println( this.self = Thread.currentThread();
this.self.getName() + ": ID => " + this.self.threadId()); while (true) {
System.out.println(this.self.getName() + ": ID => " + this.self.threadId());
}
} }
}
} }

View File

@ -1,15 +1,15 @@
package pp; package pp;
class MyWorkerCoop implements Runnable { class MyWorkerCoop implements Runnable {
private Thread self;
@Override private Thread self;
public void run() {
this.self = Thread.currentThread(); @Override
while (true) { public void run() {
System.out.println( this.self = Thread.currentThread();
this.self.getName() + ": ID => " + this.self.threadId()); while (true) {
Thread.yield(); System.out.println(this.self.getName() + ": ID => " + this.self.threadId());
Thread.yield();
}
} }
}
} }

View File

@ -1,6 +1,7 @@
package pp; package pp;
public class Starter { public class Starter {
static int WORKERS = 200; static int WORKERS = 200;
public static void main(String... args) { public static void main(String... args) {

View File

@ -1,14 +1,14 @@
package pp; package pp;
class MyWorker implements Runnable { class MyWorker implements Runnable {
private Thread self;
@Override private Thread self;
public void run() {
this.self = Thread.currentThread(); @Override
while (true) { public void run() {
System.out.println( this.self = Thread.currentThread();
this.self.getName() + ": ID => " + this.self.threadId()); while (true) {
System.out.println(this.self.getName() + ": ID => " + this.self.threadId());
}
} }
}
} }

View File

@ -1,15 +1,15 @@
package pp; package pp;
class MyWorkerCoop implements Runnable { class MyWorkerCoop implements Runnable {
private Thread self;
@Override private Thread self;
public void run() {
this.self = Thread.currentThread(); @Override
while (true) { public void run() {
System.out.println( this.self = Thread.currentThread();
this.self.getName() + ": ID => " + this.self.threadId()); while (true) {
Thread.yield(); System.out.println(this.self.getName() + ": ID => " + this.self.threadId());
Thread.yield();
}
} }
}
} }

View File

@ -1,12 +1,12 @@
package pp; package pp;
public class Starter { public class Starter {
static int WORKERS = 200; static int WORKERS = 200;
public static void main(String... args) { public static void main(String... args) {
for (var i = 0; i < Starter.WORKERS; i++) { for (var i = 0; i < Starter.WORKERS; i++) {
var t = new Thread(new MyWorkerCoop(), var t = new Thread(new MyWorkerCoop(), String.format("Worker-%03d", i));
String.format("Worker-%03d", i));
t.start(); t.start();
} }
} }

View File

@ -1,23 +1,26 @@
package pp; package pp;
public class StarterInner { public class StarterInner {
static int WORKERS = 200;
public static void main(String... args) { static int WORKERS = 200;
for (var i = 0; i < StarterInner.WORKERS; i++) {
var t = new Thread(new Runnable() {
private Thread self;
@Override public static void main(String... args) {
public void run() { for (var i = 0; i < StarterInner.WORKERS; i++) {
this.self = Thread.currentThread(); var t = new Thread(
while (true) { new Runnable() {
System.out.println(this.self.getName() + ": ID => " private Thread self;
+ this.self.threadId());
} @Override
public void run() {
this.self = Thread.currentThread();
while (true) {
System.out.println(this.self.getName() + ": ID => " + this.self.threadId());
}
}
},
String.format("Worker-%03d", i)
);
t.start();
} }
}, String.format("Worker-%03d", i));
t.start();
} }
}
} }

View File

@ -1,18 +1,21 @@
package pp; package pp;
public class StarterLambda { public class StarterLambda {
static int WORKERS = 200;
public static void main(String... args) { static int WORKERS = 200;
for (var i = 0; i < StarterLambda.WORKERS; i++) {
var t = new Thread(() -> { public static void main(String... args) {
var self = Thread.currentThread(); for (var i = 0; i < StarterLambda.WORKERS; i++) {
while (true) { var t = new Thread(
System.out.println( () -> {
self.getName() + ": ID => " + self.threadId()); var self = Thread.currentThread();
while (true) {
System.out.println(self.getName() + ": ID => " + self.threadId());
}
},
String.format("Worker-%03d", i)
);
t.start();
} }
}, String.format("Worker-%03d", i));
t.start();
} }
}
} }

View File

@ -1,15 +1,16 @@
package pp; package pp;
public class Runner { public class Runner {
public static void main(String... args) {
var task = new Task(); public static void main(String... args) {
var thread = new Thread(task); var task = new Task();
thread.setUncaughtExceptionHandler((t, e) -> { var thread = new Thread(task);
System.err.println("Unhandled Exception: " + e.getMessage()); thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println(" Thread: " + t.threadId() + " - " + t.getName()); System.err.println("Unhandled Exception: " + e.getMessage());
System.err.println(" Thread State: " + t.getState()); System.err.println(" Thread: " + t.threadId() + " - " + t.getName());
e.printStackTrace(System.err); System.err.println(" Thread State: " + t.getState());
}); e.printStackTrace(System.err);
thread.start(); });
} thread.start();
}
} }

View File

@ -1,6 +1,7 @@
package pp; package pp;
public class Task implements Runnable { public class Task implements Runnable {
private volatile Thread self; private volatile Thread self;
private volatile boolean stopped = false; private volatile boolean stopped = false;

View File

@ -1,20 +1,21 @@
package pp; package pp;
public class Runner { public class Runner {
public static void main(String... args) {
var task = new Task();
var thread = new Thread(task);
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Unhandled Exception: " + e.getMessage());
System.err.println(" Thread: " + t.threadId() + " - " + t.getName());
System.err.println(" Thread State: " + t.getState());
e.printStackTrace(System.err);
});
thread.start();
// falsch: thread läuft weiter:
// (new Thread(() -> thread.interrupt())).start();
// richtig: thread wird beendet: public static void main(String... args) {
(new Thread(() -> task.stopRequest())).start(); var task = new Task();
} var thread = new Thread(task);
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("Unhandled Exception: " + e.getMessage());
System.err.println(" Thread: " + t.threadId() + " - " + t.getName());
System.err.println(" Thread State: " + t.getState());
e.printStackTrace(System.err);
});
thread.start();
// falsch: thread läuft weiter:
// (new Thread(() -> thread.interrupt())).start();
// richtig: thread wird beendet:
(new Thread(() -> task.stopRequest())).start();
}
} }

View File

@ -1,6 +1,7 @@
package pp; package pp;
public class Task implements Runnable { public class Task implements Runnable {
private volatile Thread self; private volatile Thread self;
private volatile boolean stopped = false; private volatile boolean stopped = false;

View File

@ -17,8 +17,6 @@ public class MemoryBarrierTest extends Thread {
t.start(); t.start();
Thread.sleep(1000); Thread.sleep(1000);
t.stopped = true; t.stopped = true;
System.out.println( System.out.println("Main thread set stopped on MemoryBarrierTest-Thread.");
"Main thread set stopped on MemoryBarrierTest-Thread.");
} }
} }

View File

@ -19,8 +19,6 @@ public class MemoryBarrierTest1 extends Thread {
t.start(); t.start();
Thread.sleep(1000); Thread.sleep(1000);
t.stopped = true; t.stopped = true;
System.out.println( System.out.println("Main thread set stopped on MemoryBarrierTest-Thread.");
"Main thread set stopped on MemoryBarrierTest-Thread.");
} }
} }

View File

@ -29,8 +29,6 @@ public class MemoryBarrierTest1b extends Thread {
t.start(); t.start();
Thread.sleep(1000); Thread.sleep(1000);
t.setStopped(true); t.setStopped(true);
System.out.println( System.out.println("Main thread set stopped on MemoryBarrierTest-Thread.");
"Main thread set stopped on MemoryBarrierTest-Thread.");
} }
} }

View File

@ -21,8 +21,6 @@ public class MemoryBarrierTest2 extends Thread {
t.start(); t.start();
Thread.sleep(1000); Thread.sleep(1000);
t.stopped = true; t.stopped = true;
System.out.println( System.out.println("Main thread set stopped on MemoryBarrierTest-Thread.");
"Main thread set stopped on MemoryBarrierTest-Thread.");
} }
} }

View File

@ -13,8 +13,7 @@ public class MemoryBarrierTest5 extends Thread {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
this.stopped = true; this.stopped = true;
System.out.println( System.out.println("Stopper thread set stopped on MemoryBarrierTest-Thread.");
"Stopper thread set stopped on MemoryBarrierTest-Thread.");
}); });
this.stopper.start(); this.stopper.start();
} }
@ -40,7 +39,5 @@ public class MemoryBarrierTest5 extends Thread {
public static void main(String... args) throws InterruptedException { public static void main(String... args) throws InterruptedException {
var t = new MemoryBarrierTest5(); var t = new MemoryBarrierTest5();
t.start(); t.start();
} }
} }

View File

@ -23,8 +23,6 @@ public class MemoryBarrierTest6 extends Thread {
t.start(); t.start();
Thread.sleep(1000); Thread.sleep(1000);
t.stopped = true; t.stopped = true;
System.out.println( System.out.println("Main thread set stopped on MemoryBarrierTest-Thread.");
"Main thread set stopped on MemoryBarrierTest-Thread.");
} }
} }

View File

@ -3,6 +3,7 @@ package pp;
import java.util.Random; import java.util.Random;
public class MyThreadLocalRandom0 implements Runnable { public class MyThreadLocalRandom0 implements Runnable {
public static long now = System.currentTimeMillis(); public static long now = System.currentTimeMillis();
public Random rand = new Random(now); public Random rand = new Random(now);

View File

@ -3,6 +3,7 @@ package pp;
import java.util.Random; import java.util.Random;
public class MyThreadLocalRandom1 implements Runnable { public class MyThreadLocalRandom1 implements Runnable {
public static long now = System.currentTimeMillis(); public static long now = System.currentTimeMillis();
public ThreadLocal<Random> rand = new ThreadLocal<>() { public ThreadLocal<Random> rand = new ThreadLocal<>() {
@Override @Override

View File

@ -3,6 +3,7 @@ package pp;
import java.util.Random; import java.util.Random;
public class MyThreadLocalRandom2 implements Runnable { public class MyThreadLocalRandom2 implements Runnable {
public static long now = System.currentTimeMillis(); public static long now = System.currentTimeMillis();
public static ThreadLocal<Random> rand = new ThreadLocal<>() { public static ThreadLocal<Random> rand = new ThreadLocal<>() {
@Override @Override

View File

@ -21,9 +21,7 @@ public class Factory {
() -> { () -> {
Type object = Factory.getInstance(); Type object = Factory.getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -20,9 +20,7 @@ public class Factory0 {
() -> { () -> {
var object = getInstance(); var object = getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -20,9 +20,7 @@ public class Factory1 {
() -> { () -> {
var object = getInstance(); var object = getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -22,9 +22,7 @@ public class Factory2 {
() -> { () -> {
var object = getInstance(); var object = getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -23,9 +23,7 @@ public class Factory3 {
() -> { () -> {
var object = Factory3.getInstance(); var object = Factory3.getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -24,9 +24,7 @@ public class Factory4 {
() -> { () -> {
var object = getInstance(); var object = getInstance();
System.out.println( System.out.println(
Thread.currentThread().getName() + Thread.currentThread().getName() + ": serial of instance = " + object.getSerial()
": serial of instance = " +
object.getSerial()
); );
}, },
String.format("InstanceGrabber-%02d", i) String.format("InstanceGrabber-%02d", i)

View File

@ -0,0 +1,7 @@
# Laboraufgabe "Dinnierende Philosoph:innen"
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-01-SynchPhilosopher.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-dinierende-philosophinnen)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.01-SynchPhilosopher</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.PhilosopherExperiment</exec.mainClass>
<maven.compiler.release>19</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,3 @@
package pp;
public class Chopstick {}

View File

@ -0,0 +1,77 @@
package pp;
import java.util.Random;
public class Philosopher extends Thread {
static final int MAX_THINKING_DURATION_MS = 1000;
static final int MAX_EATING_DURATION_MS = 3000;
static final int MAX_TAKING_TIME_MS = 1000;
private final Chopstick left;
private final Chopstick right;
private final Random random;
private int eaten;
private final int seat;
private volatile boolean stop;
private void log(String message) {
synchronized (Philosopher.class) {
for (var i = 1; i <= this.seat; i++) {
System.out.print(" ");
}
System.out.println(threadId() + " " + message);
}
}
public void stopPhilosopher() {
log("stopping");
this.stop = true;
interrupt();
}
public Philosopher(int seat, Chopstick left, Chopstick right) {
this.stop = false;
this.seat = seat;
this.left = left;
this.right = right;
this.random = new Random();
this.eaten = 0;
}
@Override
public void run() {
log("starting");
try {
while (!this.stop) {
think();
eat();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log("stopped; eaten=" + this.eaten);
}
private void think() throws InterruptedException {
Thread.sleep(this.random.nextInt(MAX_THINKING_DURATION_MS));
}
private void eat() throws InterruptedException {
log("try taking left");
synchronized (this.left) {
log("left acquired");
Thread.sleep(this.random.nextInt(MAX_TAKING_TIME_MS));
log("try taking right");
synchronized (this.right) {
log("right acquired");
log("eating");
this.eaten++;
Thread.sleep(this.random.nextInt(MAX_EATING_DURATION_MS));
}
log("right released");
}
log("left released");
}
}

View File

@ -0,0 +1,25 @@
package pp;
public class PhilosopherExperiment {
static final int PHILOSOPHER_NUM = 5;
static final int EXP_DURATION_MS = 20000;
public static void main(String... args) throws InterruptedException {
var chopsticks = new Chopstick[PHILOSOPHER_NUM];
var philosophers = new Philosopher[PHILOSOPHER_NUM];
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
chopsticks[i] = new Chopstick();
}
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
philosophers[i] = new Philosopher(i, chopsticks[i], chopsticks[(i + 1) % PHILOSOPHER_NUM]);
}
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
philosophers[i].start();
}
Thread.sleep(EXP_DURATION_MS);
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
philosophers[i].stopPhilosopher();
}
}
}

View File

@ -0,0 +1,7 @@
# Laboraufgabe "Dinnierende Philosoph:innen"
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-01-SynchPhilosopher.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-dinierende-philosophinnen)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.01-SynchPhilosopher_solution</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.PhilosopherExperiment</exec.mainClass>
<maven.compiler.release>19</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,3 @@
package pp;
public class Chopstick {}

View File

@ -0,0 +1,77 @@
package pp;
import java.util.Random;
public class Philosopher extends Thread {
static final int MAX_THINKING_DURATION_MS = 1000;
static final int MAX_EATING_DURATION_MS = 3000;
static final int MAX_TAKING_TIME_MS = 1000;
private final Chopstick left;
private final Chopstick right;
private final Random random;
private int eaten;
private final int seat;
private volatile boolean stop;
private void log(String message) {
synchronized (Philosopher.class) {
for (var i = 1; i <= this.seat; i++) {
System.out.print(" ");
}
System.out.println(threadId() + " " + message);
}
}
public void stopPhilosopher() {
log("stopping");
this.stop = true;
interrupt();
}
public Philosopher(int seat, Chopstick left, Chopstick right) {
this.stop = false;
this.seat = seat;
this.left = left;
this.right = right;
this.random = new Random();
this.eaten = 0;
}
@Override
public void run() {
log("starting");
try {
while (!this.stop) {
think();
eat();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
log("stopped; eaten=" + this.eaten);
}
private void think() throws InterruptedException {
Thread.sleep(this.random.nextInt(MAX_THINKING_DURATION_MS));
}
private void eat() throws InterruptedException {
log("try taking left");
synchronized (this.left) {
log("left acquired");
Thread.sleep(this.random.nextInt(MAX_TAKING_TIME_MS));
log("try taking right");
synchronized (this.right) {
log("right acquired");
log("eating");
this.eaten++;
Thread.sleep(this.random.nextInt(MAX_EATING_DURATION_MS));
}
log("right released");
}
log("left released");
}
}

View File

@ -0,0 +1,32 @@
package pp;
public class PhilosopherExperiment {
static final int PHILOSOPHER_NUM = 5;
static final int EXP_DURATION_MS = 20000;
public static void main(String... args) throws InterruptedException {
var chopsticks = new Chopstick[PHILOSOPHER_NUM];
var philosophers = new Philosopher[PHILOSOPHER_NUM];
for (int i = 0; i < PHILOSOPHER_NUM; i++) {
chopsticks[i] = new Chopstick();
}
// first n-1 philosophers
for (var i = 0; i < (PHILOSOPHER_NUM - 1); i++) {
philosophers[i] = new Philosopher(i, chopsticks[i], chopsticks[(i + 1)]);
}
// nth philosophers: initialize with right+left instead of left+right
philosophers[PHILOSOPHER_NUM - 1] = new Philosopher(
PHILOSOPHER_NUM - 1,
chopsticks[0],
chopsticks[PHILOSOPHER_NUM - 1]
);
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
philosophers[i].start();
}
Thread.sleep(EXP_DURATION_MS);
for (var i = 0; i < PHILOSOPHER_NUM; i++) {
philosophers[i].stopPhilosopher();
}
}
}

View File

@ -0,0 +1,7 @@
# Laboraufgabe "``wait``/``notifyAll`` für Ringpuffer"
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-02-BoundedQueueWaitNotify.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-waitnotifyall-für-ringpuffer)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.02-BoundedQueueWaitNotify</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.BoundedBuffer</exec.mainClass>
<maven.compiler.release>10</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,82 @@
package pp;
public class BoundedBuffer<T> {
final Object[] items = new Object[8];
int putptr, takeptr, count;
public void put(T x) throws InterruptedException {
// solange der Speicher voll ist, warten
this.items[this.putptr] = x;
if (++this.putptr == this.items.length) {
this.putptr = 0;
}
++this.count;
// nun ist etwas neues im Speicher: Alle wartenden Threads
// benachrichtigen
}
public T take() throws InterruptedException {
// solange der Speicher leer ist, warten
@SuppressWarnings("unchecked")
var x = (T) this.items[this.takeptr];
if (++this.takeptr == this.items.length) {
this.takeptr = 0;
}
--this.count;
// nun ist etwas Platz im Speicher: Alle wartenden Threads
// benachrichtigen
return x;
}
public static void main(String... args) throws InterruptedException {
var mem = new BoundedBuffer<Integer>();
var p1 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-1"
);
var p2 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-2"
);
var c1 = new Thread(
() -> {
try {
for (var j = 1; j <= 20; j++) {
System.out.println(Thread.currentThread().getName() + ": taken=" + mem.take());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Consumer-1"
);
var now = System.currentTimeMillis();
p1.start();
p2.start();
c1.start();
p1.join();
p2.join();
c1.join();
System.out.println("Runtime: " + (System.currentTimeMillis() - now) + "ms");
}
}

View File

@ -0,0 +1,7 @@
# Laboraufgabe "``wait``/``notifyAll`` für Ringpuffer"
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-02-BoundedQueueWaitNotify.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-waitnotifyall-für-ringpuffer)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.02-BoundedQueueWaitNotify_solution</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.BoundedBuffer</exec.mainClass>
<maven.compiler.release>10</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,84 @@
package pp;
public class BoundedBuffer<T> {
final Object[] items = new Object[8];
int putptr, takeptr, count;
public synchronized void put(T x) throws InterruptedException {
while (this.count == this.items.length) {
wait();
}
this.items[this.putptr] = x;
if (++this.putptr == this.items.length) {
this.putptr = 0;
}
++this.count;
notifyAll();
}
public synchronized T take() throws InterruptedException {
while (this.count == 0) {
wait();
}
@SuppressWarnings("unchecked")
var x = (T) this.items[this.takeptr];
if (++this.takeptr == this.items.length) {
this.takeptr = 0;
}
--this.count;
notifyAll();
return x;
}
public static void main(String... args) throws InterruptedException {
var mem = new BoundedBuffer<Integer>();
var p1 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-1"
);
var p2 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-2"
);
var c1 = new Thread(
() -> {
try {
for (var j = 1; j <= 20; j++) {
System.out.println(Thread.currentThread().getName() + ": taken=" + mem.take());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Consumer-1"
);
var now = System.currentTimeMillis();
p1.start();
p2.start();
c1.start();
p1.join();
p2.join();
c1.join();
System.out.println("Runtime: " + (System.currentTimeMillis() - now) + "ms");
}
}

View File

@ -0,0 +1,7 @@
# Laboraufgabe "``await``/``signal`` für Ringpuffer”
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-03-BoundedQueueAwaitSignal.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-awaitsignal-für-ringpuffer)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.03-BoundedQueueAwaitSignal</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.BoundedBuffer</exec.mainClass>
<maven.compiler.release>10</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,92 @@
package pp;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer<T> {
final Lock lock = new ReentrantLock();
final Condition notFull = this.lock.newCondition();
final Condition notEmpty = this.lock.newCondition();
final Object[] items = new Object[8];
int putptr, takeptr, count;
public void put(T x) throws InterruptedException {
// solange der Speicher voll ist, warten
this.items[this.putptr] = x;
if (++this.putptr == this.items.length) {
this.putptr = 0;
}
++this.count;
// nun ist etwas neues im Speicher: Alle wartenden Threads
// benachrichtigen
}
public T take() throws InterruptedException {
// solange der Speicher leer ist, mit der Bedingungsvariablen notEmpty
// warten
@SuppressWarnings("unchecked")
var x = (T) this.items[this.takeptr];
if (++this.takeptr == this.items.length) {
this.takeptr = 0;
}
--this.count;
// nun ist etwas Platz im Speicher: Alle für notFull wartenden Threads
// benachrichtigen
return x;
}
public static void main(String... args) throws InterruptedException {
var mem = new BoundedBuffer<Integer>();
var p1 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-1"
);
var p2 = new Thread(
() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(Thread.currentThread().getName() + ": put=" + j);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Producer-2"
);
var c1 = new Thread(
() -> {
try {
for (var j = 1; j <= 20; j++) {
System.out.println(Thread.currentThread().getName() + ": taken=" + mem.take());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
},
"Consumer-1"
);
var now = System.currentTimeMillis();
p1.start();
p2.start();
c1.start();
p1.join();
p2.join();
c1.join();
System.out.println("Runtime: " + (System.currentTimeMillis() - now) + "ms");
}
}

View File

@ -0,0 +1,7 @@
# Laboraufgabe "``await``/``signal`` für Ringpuffer”
- [Aufgabenstellung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-03-BoundedQueueAwaitSignal.html)
- [Musterlösung](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/labs/03-Solutions.html#laboraufgabe-awaitsignal-für-ringpuffer)
# Materialien
- [Folien](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/slides/03-steuerung.html)
- [Skript](https://services.informatik.hs-mannheim.de/~s.leuchter/pp/notes/03-steuerung.html)

View File

@ -0,0 +1,12 @@
default:
mvn clean compile exec:java
exec args:
mvn exec:java -Dexec.args={{args}}
clean:
mvn clean
compile:
mvn compile
test:
mvn test
javadoc:
mvn javadoc:javadoc

View File

@ -0,0 +1,61 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pp</groupId>
<artifactId>pp.03.03-BoundedQueueAwaitSignal_solution</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<exec.mainClass>pp.BoundedBuffer</exec.mainClass>
<maven.compiler.release>10</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency><!-- für Unit-Tests -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency><!-- für Lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency><!-- für net.jcip Annotationen -->
<groupId>net.jcip</groupId>
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin><!-- für Unit-Tests [mvn test] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin><!-- [mvn compile] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
</plugin>
<plugin><!-- [mvn exec:java] -->
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin><!-- [mvn javadoc:javadoc] -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<configuration>
<show>private</show>
<locale>en_US</locale>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,96 @@
package pp;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer<T> {
final Lock lock = new ReentrantLock();
final Condition notFull = this.lock.newCondition();
final Condition notEmpty = this.lock.newCondition();
final Object[] items = new Object[8];
int putptr, takeptr, count;
public void put(T x) throws InterruptedException {
this.lock.lock();
try {
while (this.count == this.items.length) {
this.notFull.await(); // unlock falls Bedingung
}
this.items[this.putptr] = x;
if (++this.putptr == this.items.length) {
this.putptr = 0;
}
++this.count;
this.notEmpty.signal();
} finally {
this.lock.unlock();
}
}
public T take() throws InterruptedException {
this.lock.lock();
try {
while (this.count == 0) {
this.notEmpty.await();
}
@SuppressWarnings("unchecked")
var x = (T) this.items[this.takeptr];
if (++this.takeptr == this.items.length) {
this.takeptr = 0;
}
--this.count;
this.notFull.signal();
return x;
} finally {
this.lock.unlock();
}
}
public static void main(String... args) throws InterruptedException {
var mem = new BoundedBuffer<Integer>();
var p1 = new Thread(() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(
Thread.currentThread().getName() + ": put=" + j);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer-1");
var p2 = new Thread(() -> {
try {
for (var j = 1; j <= 10; j++) {
mem.put(j);
System.out.println(
Thread.currentThread().getName() + ": put=" + j);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer-2");
var c1 = new Thread(() -> {
try {
for (var j = 1; j <= 20; j++) {
System.out.println(Thread.currentThread().getName()
+ ": taken=" + mem.take());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Consumer-1");
var now = System.currentTimeMillis();
p1.start();
p2.start();
c1.start();
p1.join();
p2.join();
c1.join();
System.out.println(
"Runtime: " + (System.currentTimeMillis() - now) + "ms");
}
}