Question

Je recherche le moyen le plus simple et le plus direct de mettre en œuvre les éléments suivants:

  • Le programme principal instancie le travailleur threads pour effectuer une tâche.
  • Seules les tâches n peuvent être exécutées simultanément.
  • Lorsque n est atteint, plus de travailleurs sont commencés jusqu'au compte de les threads en cours d'exécution redescendent sous n .
Était-ce utile?

La solution

Je pense que Executors.newFixedThreadPool répond à vos besoins. Il existe différentes manières d'utiliser le service ExecutorService résultant, selon que vous voulez qu'un résultat soit renvoyé au thread principal, que la tâche soit totalement autonome et que vous ayez un ensemble de tâches à exécuter à l'avance, ou si les tâches sont mises en file d'attente en réponse à un événement.

  Collection<YourTask> tasks = new ArrayList<YourTask>();
  YourTask yt1 = new YourTask();
  ...
  tasks.add(yt1);
  ...
  ExecutorService exec = Executors.newFixedThreadPool(5);
  List<Future<YourResultType>> results = exec.invokeAll(tasks);

Sinon, si vous avez une nouvelle tâche asynchrone à exécuter en réponse à un événement, vous voudrez probablement simplement utiliser la méthode simple execute (Runnable) d'ExecutorService.

Autres conseils

/* Get an executor service that will run a maximum of 5 threads at a time: */
ExecutorService exec = Executors.newFixedThreadPool(5);
/* For all the 100 tasks to be done altogether... */
for (int i = 0; i < 100; i++) {
    /* ...execute the task to run concurrently as a runnable: */
    exec.execute(new Runnable() {
        public void run() {
            /* do the work to be done in its own thread */
            System.out.println("Running in: " + Thread.currentThread());
        }
    });
}
/* Tell the executor that after these 100 steps above, we will be done: */
exec.shutdown();
try {
    /* The tasks are now running concurrently. We wait until all work is done, 
     * with a timeout of 50 seconds: */
    boolean b = exec.awaitTermination(50, TimeUnit.SECONDS);
    /* If the execution timed out, false is returned: */
    System.out.println("All done: " + b);
} catch (InterruptedException e) { e.printStackTrace(); }

Les exécuteurs. newFixedThreadPool (int)

Executor executor = Executors.newFixedThreadPool(n);

Runnable runnable = new Runnable() {
 public void run() {
  // do your thing here
 }
}

executor.execute(runnable);

Utilisez le framework Executor; à savoir newFixedThreadPool (N)

  1. Si votre file d'attente de tâches ne va pas être dissociée et que les tâches peuvent s'achever dans des intervalles de temps plus courts, vous pouvez utiliser Executors.newFixedThreadPool (n) ; comme le suggèrent les experts.

    Le seul inconvénient de cette solution est la taille de la file d'attente des tâches non liée. Vous n'avez pas le contrôle. L'énorme accumulation de tâches dans la file d'attente réduira les performances de l'application et peut entraîner une insuffisance de mémoire dans certains scénarios.

  2. Si vous souhaitez utiliser ExecutorService et activer le vol de travail , le partage des threads de travail inactifs la charge de travail des threads de travail occupés en dérobant des tâches dans la file d'attente des tâches. Il renverra le type ForkJoinPool du service d'exécution.

      

    public static ExecutorService newWorkStealingPool (parallélisme int)

         

    Crée un pool de threads qui gère suffisamment de threads pour prendre en charge le niveau de parallélisme donné et peut utiliser plusieurs files d'attente pour réduire les conflits. Le niveau de parallélisme correspond au nombre maximal de threads activement engagés ou disponibles pour le traitement des tâches. Le nombre réel de threads peut augmenter et diminuer de manière dynamique. Un pool de vol de travail ne donne aucune garantie quant à l'ordre dans lequel les tâches soumises sont exécutées.

  3. Je préfère ThreadPoolExecutor en raison de la flexibilité des API pour contrôler de nombreux paramètres, qui contrôlent l'exécution de la tâche de flux.

    ThreadPoolExecutor(int corePoolSize, 
                           int maximumPoolSize, 
                           long keepAliveTime, 
                           TimeUnit unit, 
                           BlockingQueue<Runnable> workQueue, 
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler)
    

dans votre cas, définissez corePoolSize et maximumPoolSize sur N . Ici, vous pouvez contrôler la taille de la file d'attente des tâches, définir votre propre fabrique de threads personnalisée et votre propre politique de gestion des rejets.

Consultez la question SE relative à la gestion dynamique de la taille du pool:

Pool de threads dynamique

Si vous voulez lancer le vôtre:

private static final int MAX_WORKERS = n;
private List<Worker> workers = new ArrayList<Worker>(MAX_WORKERS);

private boolean roomLeft() {
    synchronized (workers) {
        return (workers.size() < MAX_WORKERS);
    }
}

private void addWorker() {
    synchronized (workers) {
        workers.add(new Worker(this));
    }
}

public void removeWorker(Worker worker) {
    synchronized (workers) {
        workers.remove(worker);
    }
}

public Example() {
    while (true) {
        if (roomLeft()) {
            addWorker();
        } 
    }
}

Where Worker est votre classe qui étend Thread. Chaque ouvrier appellera la méthode removeWorker de cette classe et se transmettra en tant que paramètre une fois que ce sera fait.

Cela dit, le framework Executor est bien meilleur.

Éditer: Quelqu'un veut expliquer pourquoi c'est si grave, au lieu de simplement le modéliser?

Comme d'autres l'ont déjà mentionné, la meilleure solution consiste à créer un pool de threads avec Executors class:

Toutefois, si vous souhaitez lancer le vôtre, ce code devrait vous donner une idée de la marche à suivre. En gros, ajoutez simplement chaque nouveau fil à un groupe de threads et assurez-vous de ne jamais avoir plus de N threads actifs dans le groupe:

Task[] tasks = getTasks(); // array of tasks to complete
ThreadGroup group = new ThreadGroup();
int i=0;
while( i<tasks.length || group.activeCount()>0 ) {
    if( group.activeCount()<N && i<tasks.length ) {
        new TaskThread(group, tasks[i]).start();
        i++;
    } else {
        Thread.sleep(100);
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top