Java执行器:如何设置任务优先级?

问题描述 投票:38回答:7

是否有可能将执行者执行的任务设置为优先级?我已经在JCIP中找到了一些关于可能的陈述,但是我找不到任何示例,也找不到文档中的任何相关内容。

从JCIP:

执行策略指定了任务的“什么,在哪里,何时何地”执行,包括:

  • ...
  • 应按什么顺序执行任务(FIFO,LIFO,优先顺序)?
  • ...

UPD:我意识到我并没有完全问自己想问的问题。我真正想要的是:

如何在执行程序框架中使用/模拟设置线程优先级(即thread.setPriority()是什么?

java multithreading concurrency executor thread-priority
7个回答
60
投票

[the Executor interface当前唯一的具体实现是the ThreadPoolExecutorthe ScheduledThreadpoolExecutor

而不是使用实用程序/工厂类Executors,而应使用构造函数创建实例。

您可以将BlockingQueue传递给ThreadPoolExecutor的构造函数。

BlockingQueue的一种实现,the PriorityBlockingQueue使您可以将Comparator传递给构造函数,从而使您能够确定执行顺序。


40
投票

这里的想法是在执行程序中使用PriorityBlockingQueue。为此:

  • 创建一个可以比较我们的期货的比较器。
  • 为将来创建代理以使其具有优先权。
  • 覆盖'newTaskFor',以便将所有将来包装在我们的代理中。

首先,您需要优先考虑未来:

    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();
    }
}

接下来,您需要定义比较器,以对优先级期货进行正确排序:

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);
        }
    }
}

接下来让我们假设我们有一个冗长的工作,像这样:

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;
    }
}

然后为了优先执行这些作业,代码将如下所示:

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);
        }
    }
}

这是很多代码,但这几乎是完成此任务的唯一方法。

在我的机器上,输出如下:

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

4
投票

您可以实现自己的ThreadFactory,并在ThreadPoolExecutor中像这样设置它:

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

我的OpJobThreadFactory如下所示:

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;
   }
}

3
投票

您可以将ThreadPoolExecutor与Priority阻止队列一起使用How to implement PriorityBlockingQueue with ThreadPoolExecutor and custom tasks


2
投票

您可以在ThreadFactory构造函数中指定ThreadFactory(或ThreadPoolExecutor工厂方法)。这使您可以为执行程序提供具有给定线程优先级的线程。

要为不同的作业获得不同的线程优先级,您需要将它们发送给具有不同线程工厂的执行者。


0
投票

请注意,setPriority(..)通常在Linux下运行not。有关完整的详细信息,请参见以下链接:


0
投票

只需在此讨论中加点我的贡献。我已经为特定目的实现了此http://www.akshaal.info/2008/04/javas-thread-priorities-in-linux.html,它可以在我需要的时候显式地显示在执行者的BlockingQueue(在本例中为LinkedBlockingDeque)的前面,而不必处理优先级(这可能导致死锁)并且是固定的)。

我正在使用它来管理(在Android应用程序内部)需要下载许多显示在长列表视图中的图像的情况。每当用户快速向下滚动时,执行器队列就会涌入大量图像下载请求:通过将最新请求移到队列顶部,我在加载实际在屏幕上的图像方面获得了更好的性能,从而延迟了下载以后可能需要的那些。请注意,我使用内部并发映射键(可以与图像URL字符串一样简单)将任务添加到执行程序,以便稍后可以检索它们以进行重新排序。

[还有许多其他方法可以做到这一点,并且可能过于复杂,但是它可以正常工作,而且他的Android SDK中的Facebook在其自己的工作线程队列中也做了类似的事情。

随意看一下代码并给我建议,它在一个Android项目中,但是剥离一些日志和注释将使该类成为纯Java 6。

© www.soinside.com 2019 - 2024. All rights reserved.