Domanda

C'è la possibilità di impostare la priorità ai compiti che vengono eseguiti dagli esecutori? Ho trovato alcune affermazioni in JCIP riguardo è possibile, ma non riesco a trovare alcun esempio e non riesco a trovare tutto ciò che riguarda in docs.

Da JCIP:

  

Una strategia di esecuzione specifica la   "Che cosa, dove, quando e come" del compito   esecuzione, tra cui:

     
      
  • ...
  •   
  • In che ordine deve essere eseguito compiti (FIFO, LIFO, ordine di priorità )?
  •   
  • ...
  •   

UPD : mi sono reso conto che ho chiesto non è esattamente quello che volevo chiedere. Quello che volevo veramente è:

Come usare / emulare priorità discussioni di impostazione (vale a dire ciò che è stato thread.setPriority()) con quadro esecutori?

È stato utile?

Soluzione

Attualmente le implementazioni solo concrete di dell'Executor interfacciare sono il ThreadPoolExecutor e il ScheduledThreadPoolExecutor

Invece di usare il / classe factory di utilità esecutori , è necessario creare un'istanza utilizzando un costruttore.

È possibile passare un BlockingQueue ai costruttori del ThreadPoolExecutor.

Una delle implementazioni del BlockingQueue, il PriorityBlockingQueue permette di passare un comparatore per un costruttore, in questo modo si permette di decidere l'ordine di esecuzione.

Altri suggerimenti

L'idea è quella di utilizzare un PriorityBlockingQueue nel l'esecutore. Per questo:

  • Crea un comparatore che confrontare il nostro futuro.
  • creare un proxy per il futuro di tenere una priorità.
  • Override 'newTaskFor' al fine di avvolgere ogni futuro nella nostra delega.

Prima di tutto bisogna tenere la priorità sul vostro futuro:

    class PriorityFuture<T> implements RunnableFuture<T> {

    private RunnableFuture<T> src;
    private int priority;

    public PriorityFuture(RunnableFuture<T> other, int priority) {
        this.src = other;
        this.priority = priority;
    }

    public int getPriority() {
        return priority;
    }

    public boolean cancel(boolean mayInterruptIfRunning) {
        return src.cancel(mayInterruptIfRunning);
    }

    public boolean isCancelled() {
        return src.isCancelled();
    }

    public boolean isDone() {
        return src.isDone();
    }

    public T get() throws InterruptedException, ExecutionException {
        return src.get();
    }

    public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return src.get();
    }

    public void run() {
        src.run();
    }
}

Avanti è necessario definire comparatore che avrebbe correttamente ordinamento futures prioritari:

class PriorityFutureComparator implements Comparator<Runnable> {
    public int compare(Runnable o1, Runnable o2) {
        if (o1 == null && o2 == null)
            return 0;
        else if (o1 == null)
            return -1;
        else if (o2 == null)
            return 1;
        else {
            int p1 = ((PriorityFuture<?>) o1).getPriority();
            int p2 = ((PriorityFuture<?>) o2).getPriority();

            return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
        }
    }
}

Avanti supponiamo che abbiamo un lavoro lungo come questo:

class LenthyJob implements Callable<Long> {
    private int priority;

    public LenthyJob(int priority) {
        this.priority = priority;
    }

    public Long call() throws Exception {
        System.out.println("Executing: " + priority);
        long num = 1000000;
        for (int i = 0; i < 1000000; i++) {
            num *= Math.random() * 1000;
            num /= Math.random() * 1000;
            if (num == 0)
                num = 1000000;
        }
        return num;
    }

    public int getPriority() {
        return priority;
    }
}

Quindi, al fine di eseguire questi posti di lavoro in priorità il codice sarà simile a:

public class TestPQ {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        int nThreads = 2;
        int qInitialSize = 10;

        ExecutorService exec = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
                new PriorityBlockingQueue<Runnable>(qInitialSize, new PriorityFutureComparator())) {

            protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
                RunnableFuture<T> newTaskFor = super.newTaskFor(callable);
                return new PriorityFuture<T>(newTaskFor, ((LenthyJob) callable).getPriority());
            }
        };

        for (int i = 0; i < 20; i++) {
            int priority = (int) (Math.random() * 100);
            System.out.println("Scheduling: " + priority);
            LenthyJob job = new LenthyJob(priority);
            exec.submit(job);
        }
    }
}

Questo è un sacco di codice, ma che è quasi l'unico modo in cui questo può essere realizzato.

Sulla mia macchina l'output è simile al seguente:

Scheduling: 39
Scheduling: 90
Scheduling: 88
Executing: 39
Scheduling: 75
Executing: 90
Scheduling: 15
Scheduling: 2
Scheduling: 5
Scheduling: 24
Scheduling: 82
Scheduling: 81
Scheduling: 3
Scheduling: 23
Scheduling: 7
Scheduling: 40
Scheduling: 77
Scheduling: 49
Scheduling: 34
Scheduling: 22
Scheduling: 97
Scheduling: 33
Executing: 2
Executing: 3
Executing: 5
Executing: 7
Executing: 15
Executing: 22
Executing: 23
Executing: 24
Executing: 33
Executing: 34
Executing: 40
Executing: 49
Executing: 75
Executing: 77
Executing: 81
Executing: 82
Executing: 88
Executing: 97

È possibile implementare il proprio ThreadFactory e impostarlo all'interno ThreadPoolExecutor in questo modo:

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, numOfWorkerThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.setThreadFactory(new OpJobThreadFactory(Thread.NORM_PRIORITY-2));

dove il mio aspetto OpJobThreadFactory come il seguente:

public final static class OpJobThreadFactory implements ThreadFactory {
   private int priority;
   private boolean daemon;
   private final String namePrefix;
   private static final AtomicInteger poolNumber = new AtomicInteger(1);
   private final AtomicInteger threadNumber = new AtomicInteger(1);

   public OpJobThreadFactory(int priority) {
      this(priority, true);
   }

   public OpJobThreadFactory(int priority, boolean daemon) {
      this.priority = priority;
      this.daemon = daemon;
      namePrefix = "jobpool-" +poolNumber.getAndIncrement() + "-thread-";
   }

   @Override
   public Thread newThread(Runnable r) {
      Thread t = new Thread(r, namePrefix + threadNumber.getAndIncrement());
      t.setDaemon(daemon);
      t.setPriority(priority);
      return t;
   }
}

è possibile utilizzare ThreadPoolExecutor con code di priorità di bloccaggio Come implementare PriorityBlockingQueue con compiti ThreadPoolExecutor e personalizzati

È possibile specificare un ThreadFactory nel costruttore ThreadPoolExecutor (o il metodo factory Executors). Ciò consente di fornire fili di un asse prioritario filo per l'esecutore.

Per ottenere diverse priorità dei thread per diversi lavori, avresti bisogno di mandarli a esecutori con diverse fabbriche di filo.

Si prega di essere consapevole del fatto che setPriority (..) fa normalmente non il lavoro sotto Linux. Vedere i seguenti link per tutti i dettagli:

Voglio solo aggiungere il mio po 'di contributo a questa discussione. Ho implementato questo ReorderingThreadPoolExecutor per uno scopo ben preciso, che è in grado di portare in modo esplicito alla parte anteriore della BlockingQueue del esecutore (in questo caso LinkedBlockingDeque) ogni volta che voglio e senza avere a che fare con le priorità (che può portare a situazioni di stallo e sono, in ogni caso, fisso).

Sto usando questo per gestire (all'interno di un app Android) il caso in cui devo scaricare molte immagini che vengono visualizzate in un elenco lungo. Ogni volta che l'utente scorre giù in fretta, la coda di esecutore si allaga di richieste Download image: spostando le ultime quelle sulla parte superiore della coda, ho raggiunto performance molto migliori nel caricamento delle immagini che sono in realtà sullo schermo, ritardando il download di quelli che saranno probabilmente necessarie in seguito. Si noti che io uso una chiave di mappa concomitante interna (che può essere semplice come la stringa URL immagine) per aggiungere i compiti per l'esecutore in modo che io possa recuperare in seguito per il riordino.

Non ci sarebbe potuto essere molti altri modi di fare lo stesso e forse è troppo complicata, ma funziona bene e anche Facebook nel suo SDK di Android sta facendo qualcosa di simile nella propria coda di discussioni di lavoro.

Non esitate a dare un'occhiata al codice e darmi suggerimenti, è all'interno di un progetto di Android, ma nudo alcuni tronchi e annotazioni renderebbe la pura classe Java 6.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top