作业题目:
给定一个如下Java类Konto(即银行帐号),能实现存钱einzahlen和取钱auszahlen的功能。
/* Eine Klasse für Bankkonten, die noch nicht für den nebenläufigen Zugriff angepasst wurde */
import java.util.ArrayList;
import java.util.Random;
import java.util.HashMap;
class Konto {
// Kontonummer als String
public String kontonummer;
// Kontostand
public int kontostand;
// Konstruktor
Konto(String nummer, int einlage) {
this.kontonummer = nummer;
this.kontostand = einlage;
}
// getter-Methoden:
public int getKontostand() {
return this.kontostand;
}
public String getKontonummer() {
return this.kontonummer;
}
// Auszahlen
public void auszahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " auszahlen.");
kontostand = kontostandvorher - betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " ausgezahlt.");
}
// Einzahlen
public void einzahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " einzahlen.");
this.kontostand = kontostandvorher + betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " eingezahlt.");
}
}
// Klasse für nebenläufig-ablaufende Transaktion
class Transaktion implements Runnable {
private Konto konto;
private int betrag;
private boolean istAuszahlung; // true = Auszahlung, false = Einzahlung
public Transaktion(Konto konto, int betrag, boolean auszahlung) {
this.konto = konto;
this.betrag = betrag;
this.istAuszahlung = auszahlung;
}
public void run() {
if (istAuszahlung) {
// Auszahlen
konto.auszahlen(betrag);
}
else {
// Einzahlen
konto.einzahlen(betrag);
}
}
}
public class KontoSimulation {
public static void main(String[] args) {
// Ein Konto mit initialem Kontostand 10
Konto konto = new Konto("konto", 10);
Random rnd = new Random();
int checkExecution = konto.getKontostand();
ArrayList transaktionen = new ArrayList<>();
// Starte 10 Transaktionen
for (int i = 0; i < 10; ++i) {
int betrag = 1 + rnd.nextInt(100);
boolean auszahlung = rnd.nextBoolean();
transaktionen.add(new Thread(new Transaktion(konto,betrag,auszahlung)));
checkExecution = checkExecution + (auszahlung?(-1)*betrag:betrag);
}
// Starte Threads
for (Thread t : transaktionen) {
t.start();
// Auskommentieren für Sequentielle Simulation:
// try {t.join();} catch (InterruptedException e) {}
}
// Warte auf Beenden der Threads
for (Thread t : transaktionen) {
try {t.join();} catch (InterruptedException e) {}
}
// Prüfe Ergebnis:
System.out.println("Kontostand erwartet: " + checkExecution);
System.out.println("Tatsächlicher Kontostand: " + konto.getKontostand());
//
// Testprogramm fuer Aufgabenteil e):
//KontoSimulationFuerAufgabeE.main(args);
}
}
问题
a) 这个类还不能做到并发存取钱,请你通过运用Monitor实现,可以先写出伪代码
答:
比较简单,给几个方法加上synchronized 就行
/* Eine Klasse für Bankkonten, die noch nicht für den nebenläufigen Zugriff angepasst wurde */
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
class Konto {
// Kontonummer als String
public String kontonummer;
// Kontostand
public int kontostand;
// Konstruktor
Konto(String nummer, int einlage) {
this.kontonummer = nummer;
this.kontostand = einlage;
}
// getter-Methoden:
public synchronized int getKontostand() {
return this.kontostand;
}
public synchronized String getKontonummer() {
return this.kontonummer;
}
// Auszahlen
public synchronized void auszahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " auszahlen.");
kontostand = kontostandvorher - betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " ausgezahlt.");
}
// Einzahlen
public synchronized void einzahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " einzahlen.");
this.kontostand = kontostandvorher + betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " eingezahlt.");
}
}
// Klasse für nebenläufig-ablaufende Transaktion
class Transaktion implements Runnable {
private Konto konto;
private int betrag;
private boolean istAuszahlung; // true = Auszahlung, false = Einzahlung
public Transaktion(Konto konto, int betrag, boolean auszahlung) {
this.konto = konto;
this.betrag = betrag;
this.istAuszahlung = auszahlung;
}
public void run() {
if (istAuszahlung) {
// Auszahlen
konto.auszahlen(betrag);
}
else {
// Einzahlen
konto.einzahlen(betrag);
}
}
}
/*//伪代码
* a) Monitor for Konto
* Monitor Konto {
* String kontonummer
* int Kontostand
* //Condition KontoVerfügbar;
* einzahlen(v){
* //waitC(KontoVerfügbar)
* saldo += v;
* //signalC(KontoVerfügbar)
* }
* auszahlen(v){
* //waitC(KontoVerfügbar)
* saldo -= v;
* //signalC(KontoVerfügbar)
* }
* }
* */
b)用一个条件变量ConditionVaribale kontostandErlaubt,表示账户余额足够,实现仅当余额大于-100时,才能从该账户中取钱。同样写出伪代码(这里Monitor中不同的队列 采用优先级EntryQ
/*
* MonitorB Konto {
* String kontonummer;
* int kontostand;
* condition kontostandErlaub
*
* void auszahlen(int betrag){
* if kontostand < -100 then
* wartC(kontostandErlaub)
* int kontostandVorher = getkontostand()
* kontostand = kontostand - betrag;
* if kontostand < -100 then
* signalC(kontostandErlaub) # 这步很重要,考虑下余额在-200的时候,连续两笔想取钱的操作都冻住了,这是时候来了一笔充值500的,是不是卡住的两个都应该能放行了,
* 但是没有这步的话,就只有一个能放行;又有人会问,如果不写这步,在einzahlen中用signalAllC代替行吗(即在java中用signalAll),我觉得也行。只要在auszahlen里面用始终用while代替if
* }
* void einzahlen(int betrag){
* int kontostandVorher = getkontostand()
* kontostand = kontostand + betrag
* if kontostand >= -100 then
* signalC(kontoStandErlaub)}
* }
* }
* */
// 如何保证E=W
// ,并且执行之后导致if中的条件事实上已经不满足了,但是wait中的线程依然会在之后被运行,因为它已经不在ConQ里面了,不用被判断了。
// 用了 while 是不是可以把 signalAll/signal 前面if给去掉了,是的,没啥区别
class Konto {
// Kontonummer als String
public String kontonummer;
// Kontostand
public int kontostand;
// Konstruktor
Konto(String nummer, int einlage) {
this.kontonummer = nummer;
this.kontostand = einlage;
}
// getter-Methoden:
public synchronized int getKontostand() {
return this.kontostand;
}
public synchronized String getKontonummer() {
return this.kontonummer;
}
// Auszahlen
public synchronized void auszahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " auszahlen.");
while (getKontostand() < -100){
try {wait();} catch (InterruptedException e) {return ;}
}
kontostandvorher = getKontostand();
kontostand = kontostandvorher - betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " ausgezahlt.");
if (getKontostand() >= -100){
notify();
}
}
// Einzahlen
public synchronized void einzahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " einzahlen.");
this.kontostand = kontostandvorher + betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " eingezahlt.");
if (getKontostand() >= -100){
notify();
}
}
}
c)由于是个小银行,对于账户余额中巨额存款要收手续费,为了避免手续费,我们再加一个ConditionVariable keineStrafzinsenFaellig,表示余额不至于过多,我们认为余额 >=1000即为巨额。同样写出伪代码和实现
// 如何保证E=W
// 答案是不行,entry 和 wait 同等级,用if 的话, 由于前面的signal, conditionQ 中的已经被移到waitQ里了,这时候如果插进来一个EntryQ中的线程(由于Entry优先级高)
// ,并且执行之后导致if中的条件事实上已经不满足了,但是wait中的线程依然会在之后被运行,因为它已经不在ConQ里面了,不用被判断了。
// 用了 while 是不是可以把 signalAll/signal 前面if给去掉了,是的,没啥区别
/* Monitor KontoC{
String kontonummer
int kontostand
condition kontoErlaub
condition keineStrafzinsenFaellig
void einzahlen(betrag){
if kontostand>=1000 then
wartC(keineStarfzinsenFaellig)
kontostand += betrag
if kontostand < 1000 then
signalC(keineStrafzinsenFaellig)
if kontostand >= -100 then
signalC(KontoErlaub)
}
void auszahlen(betrag){
if kontostand < -100 then
waitC(kontoErlaub)
kontostand -= betrag
if kontostand >= -100 then
signalC(kontoErlaub)
if kontostand < 1000 then
signalC(keineStrafzinsenFaellig)
}
}
*
* 由于Java 默认不支持多个 Condition Variable, 我们吧伪代码改成 适合 Java的版本, 用signalALLC/notifyAll(),就是所有困于CV的线程都能得到唤醒,都到WaitQ了,然后按顺序一个一个再次通过while检查,接着按顺序拿到锁(monitor的基本逻辑,只有一把锁)
* Monitor KontoCJava{
String kontonummer
int kontostand
condition CV
void einzahlen(betrag){
while kontostand>=1000 then
wartC(CV)
kontostand += betrag
if kontostand < 1000 then
signalAllC(CV)
if kontostand >= -100 then
signalAllC(CV)
}
void auszahlen(betrag){
while kontostand < -100 then
waitC(CV)
kontostand -= betrag
if kontostand >= -100 then
signalAllC(CV)
if kontostand < 1000 then
signalAllC(CV)
}
}
*/
/*
* Typical result:
* DEBUG: [Thread-0] möchte 788 vom Konto konto mit Kontostand 500 auszahlen.
DEBUG: [Thread-0] hat 788 vom Konto konto mit Kontostand -288 ausgezahlt.
DEBUG: [Thread-1] möchte 648 vom Konto konto mit Kontostand -288 auszahlen.
DEBUG: [Thread-1] get blocked
DEBUG: [Thread-3] möchte 346 vom Konto konto mit Kontostand -288 auszahlen.
DEBUG: [Thread-3] get blocked
DEBUG: [Thread-2] möchte 335 vom Konto konto mit Kontostand -288 auszahlen.
DEBUG: [Thread-2] get blocked
DEBUG: [Thread-4] möchte 206 auf das Konto konto mit Kontostand -288 einzahlen.
DEBUG: [Thread-4] hat 206 auf das Konto konto mit Kontostand -82 eingezahlt.
DEBUG: [Thread-2] activated
DEBUG: [Thread-2] hat 335 vom Konto konto mit Kontostand -417 ausgezahlt.
DEBUG: [Thread-3] activated
DEBUG: [Thread-3] get blocked
DEBUG: [Thread-1] activated
DEBUG: [Thread-1] get blocked
DEBUG: [Thread-5] möchte 448 vom Konto konto mit Kontostand -417 auszahlen.
DEBUG: [Thread-5] get blocked
DEBUG: [Thread-6] möchte 122 auf das Konto konto mit Kontostand -417 einzahlen.
DEBUG: [Thread-6] hat 122 auf das Konto konto mit Kontostand -295 eingezahlt.
DEBUG: [Thread-9] möchte 437 vom Konto konto mit Kontostand -295 auszahlen.
DEBUG: [Thread-9] get blocked
DEBUG: [Thread-7] möchte 717 auf das Konto konto mit Kontostand -295 einzahlen.
DEBUG: [Thread-7] hat 717 auf das Konto konto mit Kontostand 422 eingezahlt.
DEBUG: [Thread-9] activated
DEBUG: [Thread-9] hat 437 vom Konto konto mit Kontostand -15 ausgezahlt.
DEBUG: [Thread-5] activated
DEBUG: [Thread-5] hat 448 vom Konto konto mit Kontostand -463 ausgezahlt.
DEBUG: [Thread-1] activated
DEBUG: [Thread-1] get blocked
DEBUG: [Thread-3] activated
DEBUG: [Thread-3] get blocked
DEBUG: [Thread-8] möchte 514 auf das Konto konto mit Kontostand -463 einzahlen.
DEBUG: [Thread-8] hat 514 auf das Konto konto mit Kontostand 51 eingezahlt.
DEBUG: [Thread-3] activated
DEBUG: [Thread-3] hat 346 vom Konto konto mit Kontostand -295 ausgezahlt.
DEBUG: [Thread-1] activated
DEBUG: [Thread-1] get blocked
*
* 通过
* DEBUG: [Thread-1] activated
DEBUG: [Thread-1] get blocked
DEBUG: [Thread-3] activated
DEBUG: [Thread-3] get blocked
...
DEBUG: [Thread-3] activated
DEBUG: [Thread-3] hat 346 vom Konto konto mit Kontostand -295 ausgezahlt.
DEBUG: [Thread-1] activated
DEBUG: [Thread-1] get blocked
* 这几行可以看出Java notifyALL通知到的线程结果貌似是随机的,并不完全固定
* */
class Konto {
// Kontonummer als String
public String kontonummer;
// Kontostand
public int kontostand;
// Konstruktor
Konto(String nummer, int einlage) {
this.kontonummer = nummer;
this.kontostand = einlage;
}
// getter-Methoden:
public synchronized int getKontostand() {
return this.kontostand;
}
public synchronized String getKontonummer() {
return this.kontonummer;
}
// Auszahlen
public synchronized void auszahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " auszahlen.");
while (getKontostand() < -100){
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] get blocked ");
try {wait();} catch (InterruptedException e) {return ;}
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] activated ");
}
kontostandvorher = getKontostand();
kontostand = kontostandvorher - betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " ausgezahlt.");
if (getKontostand() >= -100){
notifyAll();
}
if (getKontostand() < 1000){
notifyAll();
}
}
// Einzahlen
public synchronized void einzahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " einzahlen.");
while (getKontostand() >= 1000){
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] get blocked ");
try {wait();} catch (InterruptedException e){return ;}
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] activated ");
}
kontostandvorher = getKontostand();
this.kontostand = kontostandvorher + betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " eingezahlt.");
if (getKontostand() >= -100){
notifyAll();
}
if (getKontostand() <= 1000){
notifyAll();
}
}
}
d)实现一个交易类ZweikontenTransaktion,表示当从帐号A中取钱不够时,就尝试从B账户中取钱,单独都不够,那么试试看两个加起来够不够,如果也不够,那么就等待
答:
当一个线程能够调用比如帐号A.getKontostand()时,该线程获得A对象中的锁,但是调用完该方法后,锁就不一定还是由该线程所有,有可能会被其他线程获得,因此为了保证在一个线程操作时能不被别的线程抢走锁,要用 synchronized 锁住A和B
import java.util.ArrayList;
import java.util.Random;
import java.util.HashMap;
class Konto {
// Kontonummer als String
public String kontonummer;
// Kontostand
public int kontostand;
// Konstruktor
Konto(String nummer, int einlage) {
this.kontonummer = nummer;
this.kontostand = einlage;
}
// getter-Methoden:
public synchronized int getKontostand() {
return this.kontostand;
}
public synchronized String getKontonummer() {
return this.kontonummer;
}
// Auszahlen
public synchronized void auszahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " auszahlen.");
while (getKontostand() < -100){
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] get blocked ");
try {wait();} catch (InterruptedException e) {return ;}
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] activated ");
}
kontostandvorher = getKontostand();
kontostand = kontostandvorher - betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " vom Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " ausgezahlt.");
if (getKontostand() >= -100){
notifyAll();
}
if (getKontostand() < 1000){
notifyAll();
}
}
// Einzahlen
public synchronized void einzahlen(int betrag) {
int kontostandvorher = getKontostand();
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] möchte "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostandvorher
+ " einzahlen.");
while (getKontostand() >= 1000){
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] get blocked ");
try {wait();} catch (InterruptedException e){return ;}
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] activated ");
}
kontostandvorher = getKontostand();
this.kontostand = kontostandvorher + betrag;
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] hat "
+ betrag
+ " auf das Konto "
+ kontonummer
+ " mit Kontostand "
+ kontostand
+ " eingezahlt.");
if (getKontostand() >= -100){
notifyAll();
}
if (getKontostand() <= 1000){
notifyAll();
}
}
}
// Klasse für nebenläufig-ablaufende Transaktion
class Transaktion implements Runnable {
private Konto konto;
private int betrag;
private boolean istAuszahlung; // true = Auszahlung, false = Einzahlung
public Transaktion(Konto konto, int betrag, boolean auszahlung) {
this.konto = konto;
this.betrag = betrag;
this.istAuszahlung = auszahlung;
}
public void run() {
if (istAuszahlung) {
// Auszahlen
konto.auszahlen(betrag);
}
else {
// Einzahlen
konto.einzahlen(betrag);
}
}
}
class ZweiTransaktion implements Runnable {
private Konto kontoA, kontoB;
private int betrag;
private boolean istAuszahlung; // true = Auszahlung, false = Einzahlung
static int erfolgAuszahlen = 0;
public ZweiTransaktion(Konto kontoA, Konto kontoB, int betrag) {
this.kontoA = kontoA;
this.kontoB = kontoB;
this.betrag = betrag;
}
public void run() {
synchronized (kontoA){
synchronized (kontoB) {
if (kontoA.getKontostand() >= betrag) {
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] Konto A mit Kontostand " + kontoA.getKontostand());
kontoA.auszahlen(betrag);
int vorherErfolgAuszahlen = this.erfolgAuszahlen;
this.erfolgAuszahlen = vorherErfolgAuszahlen + betrag;
return;
}
if (kontoB.getKontostand() >= betrag) {
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] Konto B mit Kontostand " + kontoB.getKontostand());
kontoB.auszahlen(betrag);
int vorherErfolgAuszahlen = this.erfolgAuszahlen;
this.erfolgAuszahlen = vorherErfolgAuszahlen + betrag;
return;
}
while (kontoA.getKontostand() + kontoB.getKontostand() < betrag) {
try {
wait();
} catch (InterruptedException e) {
return;
}
}
if (kontoA.getKontostand() + kontoB.getKontostand() >= betrag) {
System.out.println("DEBUG: [" + Thread.currentThread().getName() + "] Konto A+B mit Kontostand " +
(kontoB.getKontostand() + kontoA.getKontostand()));
int vorherkontostandA = kontoA.getKontostand();
kontoA.auszahlen(vorherkontostandA);
kontoB.auszahlen(betrag - vorherkontostandA);
int vorherErfolgAuszahlen = this.erfolgAuszahlen;
this.erfolgAuszahlen = vorherErfolgAuszahlen + betrag;
}
if (kontoA.getKontostand() + kontoB.getKontostand() >= betrag) {
notify();
}
}
}
}
}
class KontoSimulationFuerAufgabeD {
public static void main(String[] args) {
Konto kontoA = new Konto("kontoA", 3*100);
Konto kontoB = new Konto("kontoB", 3*100);
Random rnd = new Random();
int checkExecution = kontoA.getKontostand();
checkExecution += kontoB.getKontostand();
ArrayList transaktionen = new ArrayList<>();
// Starte 10 Transaktionen
for (int i = 0; i < 10; ++i) {
int betrag = 100;
transaktionen.add(new Thread(new ZweiTransaktion(kontoA,kontoB,betrag)));
}
// Starte Threads
for (Thread t : transaktionen) {
t.start();
}
// Warte auf Beenden der Threads
for (Thread t : transaktionen) {
try {t.join();} catch (InterruptedException e) {}
}
System.out.println("Kontostand: " + (kontoA.getKontostand() + kontoB.getKontostand()));
}
}
e)完善下面这个转账类中的 run方法,实现能在不同帐号间(原子操作)转账。
public class KontoSimulation {
public static void main(String[] args) {
// Ein Konto mit initialem Kontostand 10
// Konto konto = new Konto("konto", 10);
// Random rnd = new Random();
//
// int checkExecution = konto.getKontostand();
// ArrayList transaktionen = new ArrayList<>();
// // Starte 10 Transaktionen
// for (int i = 0; i < 10; ++i) {
// int betrag = 1 + rnd.nextInt(100);
// boolean auszahlung = rnd.nextBoolean();
// transaktionen.add(new Thread(new Transaktion(konto,betrag,auszahlung)));
// checkExecution = checkExecution + (auszahlung?(-1)*betrag:betrag);
// }
// // Starte Threads
// for (Thread t : transaktionen) {
// t.start();
// // Auskommentieren für Sequentielle Simulation:
// // try {t.join();} catch (InterruptedException e) {} // 使程序不并行,join 会block调用它的线程
//
// }
// // Warte auf Beenden der Threads
// for (Thread t : transaktionen) {
// try {t.join();} catch (InterruptedException e) {}
// }
// // Prüfe Ergebnis:
// System.out.println("Kontostand erwartet: " + checkExecution);
// System.out.println("Tatsächlicher Kontostand: " + konto.getKontostand());
//
// Testprogramm fuer Aufgabenteil e):
KontoSimulationFuerAufgabeE.main(args);
}
}
// ============================
// Für Aufgabenteil e)
class Ueberweisung implements Runnable {
private volatile HashMap konten;
private int betrag;
private String von;
private String nach;
public Ueberweisung(HashMap konten, int betrag, String von, String nach) {
this.konten = konten;
this.betrag=betrag;
this.von=von;
this.nach=nach;
}
public void run() {
/**todo,下面就是已完成的**/
Konto firstLock, lastLock;
if (von.compareTo(nach) < 0){
firstLock = konten.get(von);
lastLock = konten.get(nach);
}
else{
firstLock = konten.get(nach);
lastLock = konten.get(von);
}
synchronized(firstLock){
synchronized (lastLock) {
Konto vonKonto = konten.get(von);
Konto nachKonto = konten.get(nach);
vonKonto.auszahlen(betrag);
nachKonto.einzahlen(betrag);
return;
}
}
}
}
// ==================================================
class KontoSimulationFuerAufgabeE {
public static void main(String[] args) {
HashMap konten= new HashMap<>();
for(int i=0; i<10; i++) {
konten.put("konto_" + i,new Konto("konto_" +i, 100));
}
Random rnd = new Random();
System.out.println("Tests fuer Aufgabenteil e)");
int numkonten=2;
System.out.println("Test 1: 2 Konten");
ArrayList transaktionen = new ArrayList<>();
// Starte 10 Ueberweisungen
for (int i = 0; i < 10; ++i) {
int betrag = 1 + rnd.nextInt(100);
int vonInt = rnd.nextInt(numkonten);
int nachInt = vonInt;
while (nachInt == vonInt) {
nachInt = rnd.nextInt(numkonten);
}
String von = "konto_" + vonInt;
String nach = "konto_" + nachInt;
transaktionen.add(new Thread(new Ueberweisung(konten,betrag,von,nach)));
}
// Starte Threads
for (Thread t : transaktionen) {
t.start();
}
// Warte auf Beenden der Threads
for (Thread t : transaktionen) {
try {t.join();} catch (InterruptedException e) {}
}
System.out.println("Test 2: Mehr Konten");
numkonten=10;
transaktionen = new ArrayList<>();
// Starte 10 Ueberweisungen
for (int i = 0; i < 10; ++i) {
int betrag = 1 + rnd.nextInt(100);
int vonInt = rnd.nextInt(numkonten);
int nachInt = vonInt;
while (nachInt == vonInt) {
nachInt = rnd.nextInt(numkonten);
}
String von = "konto_" + vonInt;
String nach = "konto_" + nachInt;
transaktionen.add(new Thread(new Ueberweisung(konten,betrag,von,nach)));
}
// Starte Threads
for (Thread t : transaktionen) {
t.start();
}
// Warte auf Beenden der Threads
for (Thread t : transaktionen) {
try {t.join();} catch (InterruptedException e) {}
}
}
}