package ar.com.javacuriosities.threads.liveness;
import java.util.concurrent.TimeUnit;
/*
* Cuando hablamos de Starvation esto esta ligado a la prioridad de cada thread, esto ocurre cuando un thread con menor prioridad que otro no puede avanzar
* porque esta constantemente esperando para obtener a lock, aunque eso no sucede porque hay threads con prioridades mayores los cuales obtienen el lock
*/
public class Starvation {
public static void main(String[] args) {
BankAccount account1 = new BankAccount(1, 500d);
BankAccount account2 = new BankAccount(2, 500d);
int availableProcessors = Runtime.getRuntime().availableProcessors();
// Creamos muchos monitores para incrementar las posibilidades de Starvation
for (int i=0; i < availableProcessors * 2; i++) {
Thread monitor = new Thread(new BalanceMonitor(account2), "BalanceMonitor");
monitor.setPriority(Thread.MAX_PRIORITY);
monitor.start();
}
Thread transfer1 = new Thread(new TransferTask(account1, account2, 250d), "TransferTask-1");
Thread transfer2 = new Thread(new TransferTask(account1, account2, 250d), "TransferTask-2");
transfer1.setPriority(Thread.MIN_PRIORITY);
transfer2.setPriority(Thread.MIN_PRIORITY);
// Luego iniciamos las transferencias
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
// Log and Handle exception
e.printStackTrace();
}
transfer1.start();
transfer2.start();
}
private static final class TransferTask implements Runnable {
private double amount;
private BankAccount from;
private BankAccount to;
public TransferTask(BankAccount from, BankAccount to, double amount) {
this.from = from;
this.to = to;
this.amount = amount;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " Started execution");
from.transfer(to, amount);
System.out.println(Thread.currentThread().getName() + " Completed execution");
}
}
private static final class BalanceMonitor implements Runnable {
private BankAccount account;
public BalanceMonitor(BankAccount account) {
this.account = account;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Started execution");
while (true) {
if(account.getBalance() <= 0) {
// AquĆ enviamos un email, sms o alguna forma de notificaciĆ³n
break;
}
}
System.out.println(Thread.currentThread().getName() + " Account has gone too low, email sent, exiting");
}
}
private static final class BankAccount {
private int id;
private double balance;
public BankAccount(int id, double balance) {
this.id = id;
this.balance = balance;
}
public synchronized double getBalance() {
// Simulamos algo de IO como por ejemplo acceso a una DB
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
// Log and Handle exception
e.printStackTrace();
}
return balance;
}
public synchronized void withdraw(double amount) {
balance -= amount;
}
public synchronized void deposit(double amount) {
balance += amount;
}
public synchronized void transfer(BankAccount to, double amount) {
withdraw(amount);
to.deposit(amount);
}
@Override
public String toString() {
return "BankAccount [id=" + id + ", balance=" + balance + "]";
}
}
}