按顺序

时间:2017-06-19 20:08:25

标签: java multithreading concurrency

所以这是我的情况:

  • 我有一些应该做后台工作的线程,理想情况下是使用ThreadPool / ExecutorService等
  • 定期生成很多Runnables,调用一个long方法。它们应该由后台工作人员处理。
  • runnables有一个他们应该执行的命令(大约)。有趣的是:订购是动态的,可能随时改变。因此,应该在运行之前尽可能晚地确定哪个可以运行下一步。
  • 应该可以停止所有当前正在运行的runnables。如果无法做到这一点,应通知他们,以便他们在完成后放弃工作。

我真的不知道如何解决这个问题,而且在这方面我并不熟悉多线程和Java的API。

关于订购

我的意思是大约按顺序排列:如果它们按顺序启动,那就足够了。每个Runnable都会对地图的图块进行一些处理。我们的想法是以这样的方式对可运行物进行分类,首先加载在用户正在看的位置附近的瓦片,然后加载周围环境。请注意,因此执行顺序可能随时发生变化。

4 个答案:

答案 0 :(得分:1)

一种解决方案是将您要处理的所有作业放入PriorityBlockingQueue。 (此队列使用队列项的自然顺序或提供比较器自动排序)。那么在ExecutorService中运行的线程应该只从队列中获取元素。

例如

import java.util.Comparator;
import java.util.concurrent.PriorityBlockingQueue;

public class PriorityQueueExample {

    public static void main(String[] args) throws InterruptedException {
        PriorityQueueExample priorityQueueExample = new PriorityQueueExample();
        priorityQueueExample.doTheWork();

    }

    private void doTheWork() throws InterruptedException {
        PriorityBlockingQueue<Customer> queue = new PriorityBlockingQueue<>(10, new CustomerComparator());
        queue.add(new Customer("John", 5));
        queue.add(new Customer("Maria", 2));
        queue.add(new Customer("Ana", 1));
        queue.add(new Customer("Pedro", 3));


        while(queue.size() > 0){
            System.out.println(queue.take());
        }
    }
}

class CustomerComparator implements Comparator<Customer> {

    @Override
    public int compare(Customer o1, Customer o2) {
        return o1.getUrgency() - o2.getUrgency();
    }
}

class Customer {
    private String name;
    private int urgency;

    public Customer(String name, int urgency) {
        this.name = name;
        this.urgency = urgency;
    }

    public String getName() {
        return name;
    }

    public int getUrgency() {
        return urgency;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", urgency=" + urgency +
                '}';
    }
}

答案 1 :(得分:0)

1)让你的瓷砖实现Callable。您也可以让他们返回Callable

2)确定哪些是首先要加载的位置。

3)将他们或Callable传递给java.util.concurrent.ExecutorService.invokeAll

4)返回invokeAll后,获取与前一个相邻的下一组图块,然后再次调用java.util.concurrent.ExecutorService.invokeAll

5)如有必要,重复步骤4.

答案 2 :(得分:0)

您还可以使用List来模拟优先级队列。例如:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ListEmulateQueueExample {

    public static void main(String[] args) throws InterruptedException {
        ListEmulateQueueExample priorityQueueExample = new ListEmulateQueueExample();
        priorityQueueExample.doTheWork();
    }

    /**
     * uses a list to emulate a queue.
     */
    private void doTheWork() {
        List<Customer> customerList = Collections.synchronizedList(new ArrayList<>());

        Customer johnCustomer = new Customer("John", 5);
        Customer mariaCustomer = new Customer("Maria", 3);
        Customer anaCustomer = new Customer("Ana", 1);

        customerList.add(johnCustomer);
        customerList.add(mariaCustomer);
        customerList.add(anaCustomer);

        CustomerComparator customerComparator = new CustomerComparator();
        synchronized (customerList){
            customerList.sort(customerComparator);
        }


        System.out.println(customerList.remove(0));  // Ana


        johnCustomer.setUrgency(1);
        synchronized (customerList){
            customerList.sort(customerComparator);
        }

        System.out.println(customerList.remove(0));  // John

    }
}

答案 3 :(得分:0)

所以,我终于解决了这个问题。它并不那么漂亮和善良,但它按预期工作。

这个想法是:如果每个Runnable都是无状态的并且只调用一个方法,那么它不需要知道它应该在创建时处理的tile。相反,一旦它启动,它将要求所需的磁贴

public class WorldRendererGL {

    protected Map<Vector2i, RenderedRegion> regions     = new ConcurrentHashMap<>();
    protected Queue<RegionLoader>           running     = new ConcurrentLinkedQueue<>();
    protected Set<RenderedRegion>           todo        = ConcurrentHashMap.newKeySet();

    protected ExecutorService               executor;

    /** Recalculate everything */
    public void invalidateTextures() {
        //Abort current calculations
        running.forEach(f -> f.invalid.set(true));
        running.clear();
        todo.addAll(regions.values());

        for (int i = 0; i < regions.size(); i++) {
            RegionLoader loader = new RegionLoader();
            running.add(loader);
            executor.submit(loader);
        }
    }

    protected class RegionLoader implements Runnable {

        /** Set this to true to nullify all calculations*/
        final AtomicBoolean invalid = new AtomicBoolean(false);

        @Override
        public void run() {
            try {
                if (invalid.get())
                    return;
                RenderedRegion region = null;
                region = nextRegion(); // Get the correct work at runtime
                if (region == null)
                    return;
                BufferedImage texture = renderer.renderRegion(new RegionFile(region.region.regionFile));
                if (!invalid.get()) {
                    region.texture = texture;
                    update.notifyObservers();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    protected RenderedRegion nextRegion() {
        Comparator<RenderedRegion> comp = (a, b) -> /*...*/);
        RenderedRegion min = null;
        for (Iterator<RenderedRegion> it = todo.iterator(); it.hasNext();) {
            RenderedRegion r = it.next();
            if (min == null || comp.compare(min, r) > 0)
                min = r;
        }
        todo.remove(min);
        return min;
    }
}