package fr.uga.miashs.compsynchro;

import java.text.DateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;


interface SharedValue extends Runnable {
	public int get();
}

/**
 * Valeur partagée sans verouillage
 *
 */
class SharedValueNoLock implements SharedValue {

	int sharedValue=0;
	int nbIterations;
	
	public SharedValueNoLock(int nbIterations) {
		this.nbIterations=nbIterations;
	}
	
	@Override
	public void run() {
		for (int i=0 ; i<nbIterations ; i++) {
			sharedValue++;
		}
	}
	
	public int get() { return sharedValue; }
}

/**
 * Valeur partagée sans verouillage mais avec mot clé volatile (force à relire la donnée de la RAM).
 *
 */
class SharedValueNoLockVolatile implements SharedValue {

	volatile int sharedValue=0;
	int nbIterations;
	
	public SharedValueNoLockVolatile(int nbIterations) {
		this.nbIterations=nbIterations;
	}
	
	@Override
	public void run() {
		for (int i=0 ; i<nbIterations ; i++) {
			sharedValue++;
		}
	}
	
	public int get() { return sharedValue; }
}

/**
 * Valeur partagée avec incrémentation en exlcusion mutuelle avec mot clé synchronized
 *
 */
class SharedValueSynchronized implements SharedValue {

	volatile int sharedValue=0;
	int nbIterations;
	Object lock = new Object();
	
	public SharedValueSynchronized(int nbIterations) {
		this.nbIterations=nbIterations;
	}
	
	@Override
	public void run() {
		for (int i=0 ; i<nbIterations ; i++) {
			synchronized (lock) {
				sharedValue++;
			}
		}
	}
	public int get() { return sharedValue; }
}

/**
 * Valeur partagée avec incrémentation en exlcusion mutuelle avec un ReentrantLock
 *
 */
class SharedValueLock implements SharedValue {

	volatile int sharedValue;
	int nbIterations;
	ReentrantLock lock;
	
	public SharedValueLock(int nbIterations) {
		sharedValue = 0;
		lock = new ReentrantLock();
		this.nbIterations=nbIterations;
	}
	
	@Override
	public void run() {
		for (int i=0 ; i<nbIterations ; i++) {
			lock.lock();
			sharedValue++;
			lock.unlock();
		}
	}
	public int get() { return sharedValue; }
}

/**
 * Valeur partagée sans exclusion mutuelle mais avec instruction d'incrémentation atomique (CAS)
 *
 */
class SharedValueCAS implements SharedValue {

	AtomicInteger sharedValue;
	int nbIterations;
	
	public SharedValueCAS(int nbIterations) {
		sharedValue = new AtomicInteger();
		this.nbIterations=nbIterations;
	}
	
	@Override
	public void run() {
		for (int i=0 ; i<nbIterations ; i++) {
			sharedValue.incrementAndGet();
		}
	}
	public int get() { return sharedValue.get(); }
}

public class IncrementInteger {

	public static void main(String[] args) {
		
		/* Ici vous pouvez tester les différentes mécanismes (avec et sans exclusion mutuelle)
		 * SharedValueNoLock, SharedValueNoLockVolatile, SharedValueSynchronized, SharedValueLock, SharedValueCAS
		 * 
		 */
		SharedValue v = new SharedValueCAS(1_000_000);
		
		List<Thread> l = new ArrayList<>();
		
		long before = System.currentTimeMillis();
		for (int i=0 ; i<100 ; i++) {
			Thread t = new Thread(v);
			l.add(t);
			t.start();
		}
		
		for (Thread t : l) {
			try {
				t.join();
			}
			catch (InterruptedException e) {}
		}
		long duration = System.currentTimeMillis()-before;
		DateFormat df = DateFormat.getTimeInstance();
		System.out.println(duration);
		System.out.println(v.get());
	}
	
}
