Question

Nous avons un ensemble d'actions ou "Emplois". ce que nous aimerions arriver un à la fois (pas simultanément). C'est-à-dire que le travail A ne peut pas se produire tant que B se produit, et que deux travaux C ne peuvent pas s'exécuter simultanément.

Si un thread tente d'exécuter un travail simultanément, il devrait recevoir une erreur. Nous ne devrions pas simplement faire la queue de la demande.

Certains travaux ont lieu de manière asynchrone lorsque l'utilisateur en fait la demande, nous renvoyons un message d'état et un identifiant, puis nous traitons le travail de manière asynchrone sur le serveur.

Nous recherchons des conseils pour gérer ce scénario.

Une option consiste à verrouiller un objet partagé :

public class Global {
  public static final Object lock = new Object();
}

public class JobA {
    public void go() {
        synchronized(Global.lock) {
            //Do A stuff
        }
    }
}

public class JobB {
    public void go() {
        synchronized(Global.lock) {
            //Do B stuff
        }
    }
}

Problèmes avec ceci:

  1. Nous ferions la queue pour les demandes simultanées et ne renverrions pas de message d'erreur (ce que nous voulons faire)
  2. Si JobA veut écrire un message dans une file d'attente à traiter de manière asynchrone, comment peut-on être sûr que lorsque le message est lu dans la file d'attente par une autre méthode dans JobA , la méthode pourra acquérir le verrou Global.lock avant qu'une autre instance de JobA ne démarre??

Des suggestions pour une meilleure approche?

Était-ce utile?

La solution

On dirait que ce dont vous avez besoin est un verrou exclusif permettant d'éviter le blocage lors de la tentative d'acquisition du verrou.

Heureusement, l'interface de verrouillage a une méthode tryLock afin que vous puissiez faire ce qui suit.

final Lock lock = new ReentrantLock();

....

void attemptJob( Runnable runnable )
{
    if (!lock.tryLock())
        throw new ConcurrentJobException();

    try
    {
       runnable.run();
    }
    finally
    {
       lock.unlock();
    }
}

Si vous voulez exécuter async, vous devez déléguer le Runnable (ou Callable si vous préférez) à un ExecutorService et, quand il est terminé, relâchez le verrou.

EDIT: par exemple (plus quelques déclarations finales)

final Lock lock = new ReentrantLock();
final ExecutorService service = Executors.newSingleThreadExecutor();

....

void attemptJob( final Runnable runnable )
{
    if (!lock.tryLock())
        throw new ConcurrentJobException();

    service.execute( new Runnable()
    {
        public void run()
        {
            try
            {
                runnable.run();
            }
            finally
            {
                lock.unlock();
            } 
        }
    });
}

N'oubliez pas d'arrêter () le service d'exécution lorsque vous avez terminé.

Autres conseils

Avez-vous envisagé d'utiliser un SingleThreadExecutor ? Comme son nom l'indique, il utilise un seul thread pour exécuter Callables (similaire à Runnables - voir la doc) et répond donc à vos besoins en matière de traitement synchrone. L'utilisation des classes java.util.concurrent disponibles simplifiera énormément votre code de threading.

Pour gérer le scénario lors de la soumission d'un travail lorsqu'un autre travail est en cours d'exécution et générer une erreur, définissez une file d'attente limitée et un gestionnaire de rejet. Voir cette page et la section 8.3.2 / 8.3.3 spécialement pour plus de détails sur ThreadPoolExecutor.setRejectedExecutionHandler () .

Consultez le lot de printemps . Je pense que cela peut vous aider.

Une autre possibilité consiste à utiliser un ThreadPoolExecutor configuré. Je pense que cela fonctionnera, mais j'encourage vivement les lecteurs à le tester par eux-mêmes.

Executor executor = new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>());

Ce qui précède correspond à peu près à ce que newSingleThreadExecutor fait dans la classe Executors.

Pour corriger l'erreur " erreur sur une tâche simultanée " Je pense que vous pouvez simplement modifier l’implémentation de la file d’attente en SynchronousQueue.

Executor executor = new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new SynchronousQueue<Runnable>());

Ceci devrait permettre l'exécution d'une tâche. Si une seconde, si elle est soumise avant la première, se termine, ThreadPoolExecutor RejectedExecutionHandler est appelée, ce qui déclenche par défaut une exception RejectedExecutionException.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top