Thread et processus
Processus concerne une application, un programme
Un programme peut gérer plusieurs petits procesus appelé threads et partagés ses ressources au niveau de la JVM du cluster issu du serveur applicatif
Probleme couramment rencontré en informatique
Les sémaphores: utiliser beaucoup dans la gestion des ressources dans son système exploitation .
Le problemes du diner des philosophes: algorithme de Dijkstra
L’algorithme du banquier.
Mot clé volatile
uniquement utilisé dans le cas de variable primitive, unique que plusieurs thread peuvent attaquées cette meme variable
Attention ce mot clé ne garanti pas l’atomicité de l’opération
Pas de lock associé dessus
Attention au NPE si la valeur est à null
(Cela indique que la memoire est partagé entre les threads pour cette variable)
Difference entre volatile et synchronized
Synchronized s’applique à un objet et volatile s’applique à la référence de objet
Pour garantir qu’une variable primitive est atomique CF classe AtomiInteger / AtomicReference
Utilisation synchronized
Exemple Simple : utilisation des mots clé : notify , wait…
Dans cet exemple nous voyons qu’en faites nous appliqueons de manière intuitive le pattern observateur . Dans lexemple suivant nous allons approfondir cette notion
public class AutoBus extends Thread {
int total = 0;
public void run() {
synchronized (this) {
System.out.println("wait ...");
for (int i = 0; i < 10; i++){
total = +i;
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("passenger is given notification call ");
notify();
}
}
public static void main(String[] args) throws InterruptedException {
AutoBus bus = new AutoBus();
bus.start();
synchronized (bus) {
System.out.println(" passenger is waiting for the bus");
bus.wait();
System.out.println("passenger go notification");
}
System.out.println(" total=" + bus.total);
}
}
Préferer les block synchronized au méthode synchronized .
Ne crée pas de lock au niveau de objet équivalent à synchronized(this) cela veut dire que l’on utilise le monitor of the « this » objet in order to help keeping the object safe . C’est une erreur !!
La bonne pratique est de créer un objet privé qui serviera de monitoring de l’objet et nous garantiera que celui ci nest pas modifié par deux threads concurents :
exemple ligne :
private final Object myMonitor = new Object();
package observerpattern;
/**
*
* @author Hugo
*/
public interface ISubject {
void registerObserver(IObserver o);
void unregisterObserver(IObserver o);
void NotifyObservers(String s);
}
public class Subject implements ISubject {
private final HashSet<IObserver> subscribers;
public Subject() {
// Eliminates the possibility of duplicated objects - subscribers.
this.subscribers = new HashSet<IObserver>();
}
// // 1) Using the "this" object's monitor
// @Override // Registering subscriber.
// public synchronized void registerObserver(IObserver o) {
// System.out.println("Inside register observer method");
// this.subscribers.add(o);
// }
//
// @Override // Unregistering subscriber.
// public synchronized void unregisterObserver(IObserver o) {
// this.subscribers.remove(o);
// }
//
// @Override
// public void NotifyObservers(final String s) {
// // Equivalent to --> public synchronized void NotifyObservers(final String s)
// synchronized(this){
// for (IObserver o : this.subscribers) {
// o.Update(s);
// }
// }
// }
// 2) Using a private object's monitor!
private final Object myMonitor = new Object();
@Override // Registering subscriber.
public void registerObserver(IObserver o) {
synchronized ( myMonitor ){
System.out.println("Inside register observer method");
this.subscribers.add(o);
}
}
@Override // Unregistering subscriber.
public void unregisterObserver(IObserver o) {
synchronized ( myMonitor ){
this.subscribers.remove(o);
}
}
@Override
public void NotifyObservers(final String s) {
synchronized ( myMonitor ){
for (IObserver o : this.subscribers) {
o.Update(s);
}
}
}
}
package observerpattern;
/**
*
* @author Hugo
*/
public interface IObserver {
void Update(String s);
void SubscribeTo(ISubject subject);
void UnsubscribeTo(ISubject subject);
}
public class Observer implements IObserver{
private final String _name;
public Observer(String name){
_name = name;
}
@Override
public void Update(String s){
System.out.println(String.format("%s has received a new message from magazine: %s",_name,s));
}
@Override
public void SubscribeTo(ISubject subject) {
subject.registerObserver(this);
}
@Override
public void UnsubscribeTo(ISubject subject) {
subject.unregisterObserver(this);
}
}
Callable et Futur
Java 5 introduced java.util.concurrent.Callable interface in concurrency package that is similar to Runnable interface but it can return any Object and able to throw Exception.
Callable interface use Generic to define the return type of Object. Executors class provide useful methods to execute Callable in a thread pool. Since callable tasks run in parallel, we have to wait for the returned Object. Callable tasks return java.util.concurrent.Future object. Using Future we can find out the status of the Callable task and get the returned Object. It provides get() method that can wait for the Callable to finish and then return the result.
package callable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(1000);
//return the thread name executing this callable task
return Thread.currentThread().getName();
}
public static void main(String args[]){
//Get ExecutorService from Executors utility class, thread pool size is 10
ExecutorService executor = Executors.newFixedThreadPool(10);
//create a list to hold the Future object associated with Callable
List<Future<String>> list = new ArrayList<Future<String>>();
//Create MyCallable instance
Callable<String> callable = new MyCallable();
for(int i=0; i< 100; i++){
//submit Callable tasks to be executed by thread pool
Future<String> future = executor.submit(callable);
//add Future to the list, we can get return value using Future
list.add(future);
}
for(Future<String> fut : list){
try {
//print the return value of Future, notice the output delay in console
// because Future.get() waits for task to get completed
System.out.println(new Date()+ "::"+fut.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
//shut down the executor service now
executor.shutdown();
}
}
Multithreading in a loop
public class ExecutorServiceExample {
private static class MyTask implements Callable<Double> {
private final int index;
private MyTask(Integer idx) { index = idx; }
public Double call() throws InterruptedException {
System.out.println("Working on index " + index + "...");
// pretend we're doing something long and complex here...
Thread.sleep(2000);
System.out.println("Completed index " + index);
// pretend this is the net result of the long, complex thing we did:
return index * .001;
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService execs = Executors.newFixedThreadPool(10);
List<Future<Double>> results = new ArrayList<Future<Double>>();
for (int i = 0; i < 100; i++) {
Future<Double> result = execs.submit(new MyTask(i));
results.add(result);
}
// this is just a signal; it won't terminate until all tasks are completed
execs.shutdown();
System.out.println("Sent shutdown signal");
execs.awaitTermination(60, TimeUnit.MINUTES);
System.out.println("Results: ");
int index = 0;
for (Future<Double> result : results) {
System.out.println(index++ + ":\t" + result.get());
}
}
}
Autre resources
http://www.journaldev.com/1034/java-blockingqueue-example-implementing-producer-consumer-problem
http://doanduyhai.wordpress.com/2012/08/04/design-pattern-the-asynchronous-dispatcher/
http://www.journaldev.com/1162/