Created
October 21, 2015 06:52
-
-
Save VincentTatan/eca472d1d77dcc9ce723 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.locks.Lock; | |
import java.util.concurrent.locks.ReentrantLock; | |
import java.io.*; | |
/** | |
* The Account class: | |
* 1) has a current balance and a name | |
* 2) allows transfers to another account | |
* | |
* Important properties: | |
* 1) The Account class is not thread safe. This means that running multiple transfers (from or to) the same account | |
* concurrently may result in unexpected outcomes (such as less debit or less credit than expected). | |
* | |
* Important options: | |
* 1) SLOW - there is a static variable "SLOW" which determines how much processor time transfers take. When set | |
* to true, the computational load of a transfer is high. When set to false, the load will be low. Setting to | |
* true will increase the performance impact of parallelism. Setting to false will increase the likelihood of | |
* race conditions. | |
* | |
* 2) onDisk - object variable determines if the balance is saved to (and read from) disk on each action. | |
*/ | |
public class Account | |
{ | |
private static Lock lock = new ReentrantLock(); | |
public static boolean SLOW = false; | |
private boolean onDisk = false; | |
public Account(float balance, String name, boolean onDisk) | |
{ | |
this.balance = balance; | |
this.name = name; | |
this.onDisk = onDisk; | |
if (onDisk) saveToDisk(); | |
} | |
/** | |
* This versions requires that the balance is never negative. This is sensible in that | |
* Amy should never be allowed to transfer more money to Betty than Amy actually has. | |
* If transfers can interleave, then two transfers from Amy can both pass the if statement | |
* even if there is only enough money for one of them. | |
* | |
*/ | |
public void transfer(float amount, Account to) | |
{ | |
if (amount < balance) | |
{ | |
Thread.yield(); //this just makes the multithreading issues more common | |
this.debit(amount); | |
to.credit(amount); | |
if (SLOW) complicatedComputation(); | |
} | |
if (balance < 0) | |
{ | |
System.out.println("Negative balance detected: balance = " + balance); | |
} | |
} | |
/** | |
* to make a problem more likely, add either of the lines below in between 1 & 2: | |
* Thread.yield(); | |
* System.out.println("got balance: " + balance); | |
* | |
* to make problems less likely try with just: | |
* balance -= amount; | |
* then run a few times. you (may) see that though less common, problems still happen! | |
*/ | |
private void debit (float amount) | |
{ | |
lock.lock(); | |
float balance = getBalance(); //1 | |
balance = balance - amount; //2 | |
saveBalance(balance); //3 | |
lock.unlock(); | |
} | |
private void credit(float amount) | |
{ | |
lock.lock(); | |
float balance = getBalance(); | |
balance = balance + amount; | |
saveBalance(balance); | |
lock.unlock(); | |
} | |
//------------------------------------------------------------ | |
private float balance; | |
private String name; | |
public float getBalance() | |
{ | |
if (onDisk) | |
return readFromDisk(); | |
else | |
return balance; | |
} | |
/** | |
* saves balance to storage, in this case just "this.balance", but in practice could | |
* be a DB, another system, a file, ... | |
*/ | |
private void saveBalance(float amount) | |
{ | |
this.balance = amount; | |
if (onDisk) saveToDisk(); | |
} | |
private void complicatedComputation() | |
{ | |
float j = 1; | |
for (long i = 0; i < 110000; i++) { j += i % 15; } | |
} | |
private void saveToDisk() | |
{ | |
PrintWriter writer = null; | |
try{ | |
writer = new PrintWriter(new BufferedWriter(new FileWriter(name + ".txt", false))); | |
writer.println(balance); | |
writer.flush(); | |
writer.close(); | |
}catch(Exception e){ | |
System.err.println(e.getMessage()); | |
e.printStackTrace(); | |
} | |
} | |
private float readFromDisk() | |
{ | |
BufferedReader br = null; | |
String s = null; | |
try{ | |
br = new BufferedReader(new FileReader(name + ".txt")); | |
s = br.readLine(); | |
br.close(); | |
}catch (IOException e){ | |
System.err.println(e.getMessage()); | |
e.printStackTrace(); | |
} | |
try | |
{ | |
float b = Float.parseFloat(s); | |
return b; | |
} catch (Exception e) { | |
return balance; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment