Outils pour utilisateurs

Outils du site


lang:java:thread

Différentes classes

Runnable

public class T implements Runnable
{
  public void run() { }
}
public static void main(String[] args)
{
  Thread t1 = new Thread(new T());
  t1.start();
}

Thread

public class T extends Thread
{
  public void run() { }
}
 
public static void main(String[] args)
{
  T t1 = new T();
  t1.start();
}

FutureTask

Une FutureTask doit renvoyer un type. Mettre Void et return null si le type de retour n'est pas utilisé.

FutureTask s'exécute depuis un Executor et la valeur de retour est récupérée via la méthode bloquante get.

Quand une exception est générée, elle n'est pas remontée au thread principale. Elle est transformée en une ExecutionException qui sera générée lors de l'appel à get.

import java.util.concurrent.FutureTask;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutionException;
 
public class MyClass {
  public static void main(String args[]) {
    FutureTask<Void> futureTask = new FutureTask<Void> (() -> {
      throw new IOException("OUPS");
    });
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.execute(futureTask);
    try {
      futureTask.get();
    } catch (InterruptedException e) {
    } catch (ExecutionException e) {
      e.printStackTrace();
    }
    System.out.println("OK");
    System.out.println("OK2");
    executor.shutdown();
  }
}

Sortie standard :

OK
OK2

java.util.concurrent.ExecutionException: java.io.IOException: OUPS
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
	at MyClass.main(MyClass.java:15)
Caused by: java.io.IOException: OUPS
	at MyClass.lambda$main$0(MyClass.java:10)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

Si l'exception doit être remontée au thread principale, il faut créer une classe qui hérite de FutureTask et surchargée la méthode done(), appeler le get() avec le try/catch associé et générer une autre exception (AssertionError par exemple).

import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
 
public class MyClass {
  public static class SecuredFutureTask<V> extends FutureTask<V> {
    public SecuredFutureTask(Callable<V> callable) {
      super(callable);
    }
 
    @Override
    protected void done() {
      try {
        get();
      } catch (ExecutionException | InterruptedException e) {
        throw new AssertionError(e);
      }
    }
  }
  public static void main(String args[]) {
    FutureTask<Void> futureTask =
        new SecuredFutureTask<Void>(() -> { throw new IOException("OUPS"); });
    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.execute(futureTask);
    System.out.println("OK");
    executor.shutdown();
  }
}

Sortie standard :

OK

Exception in thread "pool-1-thread-1" java.lang.AssertionError: java.util.concurrent.ExecutionException: java.io.IOException: OUPS
	at MyClass$SecuredFutureTask.done(MyClass.java:19)
	at java.base/java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:381)
	at java.base/java.util.concurrent.FutureTask.setException(FutureTask.java:250)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:269)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.util.concurrent.ExecutionException: java.io.IOException: OUPS
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
	at MyClass$SecuredFutureTask.done(MyClass.java:17)
	... 6 more
Caused by: java.io.IOException: OUPS
	at MyClass.lambda$main$0(MyClass.java:25)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	... 3 more

Accès concurrent

class C
{
  synchronized void p(){ }
}

est équivalent à

class C
{
  void p()
  {
    synchronized (this){ }
  }
}

Le concept consiste à poser un lock sur une instance ou sur une classe.

Concurrency in JDK 5.0 Archive au 30/09/2019, version du 23/11/2004

Barrières

Semaphore

Barrière en attente d'un signal.

import java.util.concurrent.Semaphore;
 
public class MyClass {
  public static void main(String args[]) {
    Semaphore semaphoreAcquisitionReady = new Semaphore(0);
 
    new Thread(() -> {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {}
      System.out.println("BEFORE release");
      semaphoreAcquisitionReady.release();
      System.out.println("AFTER release");
    }).start();
 
    System.out.println("BEFORE acquire");
    try {
      semaphoreAcquisitionReady.acquire();
    } catch (InterruptedException e) {}
    System.out.println("AFTER acquire");
 
    semaphoreAcquisitionReady.release();
  }
}

Sortie standard.

BEFORE acquire
BEFORE release
AFTER release
AFTER acquire

Debug

Nommage des threads

Par défaut, le nom d'un thread est Thread-0 / Thread-1. Si le thread tourne dans un pool : pool-1-thread-1.

Dans le cas des threads, cela se fait directement depuis le constructeur.

new Thread(() -> {}, "Nom du thread").start();

Dans le cas de Runnable qui se lancent dans un pool de threads, il faut modifier le nom directement dans la méthode exécutée par le thread.

new FutureTask<>(() -> {
  Thread.currentThread().setName("Nom du thread");
});
lang/java/thread.txt · Dernière modification : 2023/06/12 15:07 de root