web-dev-qa-db-fra.com

Comment mettre en œuvre un filetage simple avec un nombre fixe de threads ouvriers

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

  • Le programme principal instance des threads travailleurs à faire une tâche.
  • Seules les tâches n peuvent être exécutées à la fois.
  • Lorsque n est atteint, plus de travailleurs ne sont pas démarrés jusqu'à ce que le nombre de threads d'exécution tombe en dessous de n.
50
shsteimer

Je pense que exécuteurs.newfixedthreadpool Convient à vos besoins. Il existe un certain nombre de façons différentes d'utiliser l'exécuteur d'exécution résultante, selon que vous souhaitiez un résultat renvoyé sur le fil principal ou si la tâche est totalement autonome et si vous avez une collection de tâches à effectuer à l'avant, ou Si les tâches sont 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 à effectuer en réponse à un événement, vous souhaitez probablement simplement utiliser la méthode SIMPLE execute(Runnable) [)] de l'exécuteur.

54
erickson
/* 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(); }
25
Fabian Steeg

exécuteurs.newfixedthreadpool (int)

Executor executor = Executors.newFixedThreadPool(n);

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

executor.execute(runnable);
7
Matt

Utiliser le cadre exécutant; nommément Newfixedthreadpool (N)

2
hazzen
  1. Si votre file d'attente de tâches ne sera pas non liée et que les tâches peuvent compléter dans des intervalles de temps plus courts, vous pouvez utiliser Executors.newFixedThreadPool(n); comme suggère des experts.

    Le seul inconvénient de cette solution est une taille de la file d'attente des tâches sans bornes. Vous n'avez pas de contrôle dessus. L'énorme empilement de la file d'attente des tâches dégradera les performances d'application et peut causer une mémoire dans certains scénarios.

  2. Si vous souhaitez utiliser ExecutorService et activer work stealing Mécanisme où les threads de travailleur inactif partagent la charge de travail des threads de travailleurs occupés en volant des tâches dans la file d'attente. Il retournera le type de service exécutif de Forkjoinpool.

    statique publique ExecutorService newWorkStealingPool (Int parallélisme)

    Crée un piscine de fil qui maintient suffisamment de threads pour soutenir le niveau de parallélisme donné et utiliser plusieurs files d'attente pour réduire la conflit. Le niveau de parallélisme correspond au nombre maximal de threads activement engagés, ou disponible pour s'engager dans le traitement des tâches. Le nombre réel de threads peut augmenter et réduire de manière dynamique. Une piscine de vol de travail ne garantit aucune garantie sur l'ordre dans lequel des tâches soumises sont exécutées.

  3. Je préfère ThreadPoolExecutor en raison de la flexibilité dans API pour contrôler de nombreux paratères, qui contrôle 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 les deux corePoolSize and maximumPoolSize as N. Ici, vous pouvez contrôler la taille de la file d'attente de la tâche, définir votre propre politique de gestionnaire de filetage personnalisée et votre politique de réjection.

Jetez un coup d'œil à la question de la SE connexe pour contrôler la taille de la piscine de manière dynamique:

piscine de thread dynamique

1
Ravindra babu

Si vous voulez rouler 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();
        } 
    }
}

Où travailleur est votre classe qui étend le fil. Chaque travailleur appellera la méthode Supprimer Whorker de cette classe, se transmettra en tant que paramètre, quand il est fini de faire sa chose.

Avec cela, le cadre exécutant semble beaucoup mieux.

EDIT: Quiconque se soucie d'expliquer pourquoi c'est tellement mauvais, au lieu de simplement l'abat-dire?

0
rjohnston

Comme d'autres personnes ont mentionné, votre meilleur pari est de créer une piscine de fil avec la classe exécuteurs classe:

Cependant, si vous voulez rouler le vôtre, ce code devrait vous donner une idée de la procédure à suivre. Fondamentalement, ajoutez simplement chaque nouveau thread à un groupe de fil et assurez-vous que vous n'avez jamais 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);
    }
}
0
Eli Courtwright