Java Executorservice和Runnable

Java Executorservice和Runnable,java,Java,我有一个列表,其中包含每个像素要计算的数据(例如,列表大小=1024x768)。现在我想在列表中迭代多线程,并将每个像素的计算结果保存在HashMap中。但无论我做什么,我都无法做到正确。我尝试了几种方法,最后一种是: ConcurrentMap<T, Color> map = new ConcurrentHashMap<T, Color>(); ExecutorService pool = Executors.newFixedThrea

我有一个列表,其中包含每个像素要计算的数据(例如,列表大小=1024x768)。现在我想在列表中迭代多线程,并将每个像素的计算结果保存在HashMap中。但无论我做什么,我都无法做到正确。我尝试了几种方法,最后一种是:

        ConcurrentMap<T, Color> map = new ConcurrentHashMap<T, Color>();

        ExecutorService pool = Executors.newFixedThreadPool(4);

        Iterator<T> it = camera.iterator();
        while (it.hasNext()) {
            Runnable run = () -> {
                int i = 0;
                while (it.hasNext() && i < 1000) {
                    i++;
                    T cameraRay = it.next();
                    if (object.collide(cameraRay.getRay()) == null)
                        map.put(cameraRay, BG_COLOR);
                    else
                        map.put(cameraRay, this.shader.shade(cameraRay.getRay(), object.collide(cameraRay.getRay())).getColor());
                }
            };
            pool.execute(run);
        }
        pool.shutdown();
        try {
            if (pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {
                System.out.println("Mapsize: " + map.size());
                // Draw Image:
                map.forEach((ray, color) -> {image.setColor(ray, color);});
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
编辑2: 根据沃克斯特的回答,我尝试了以下方法

Iterator<T> it = camera.iterator();
List<T> buffer = new ArrayList<T>(1000);
while (it.hasNext()) {
    buffer.add(it.next());
    if (buffer.size() >= 1000 || !it.hasNext()) {
        Runnable run = () -> {
            for (T cameraRay : buffer) {
                if (object.collide(cameraRay.getRay()) == null) // No collision
                    map.put(cameraRay, BG_COLOR);
                else
                    map.put(cameraRay, this.shader.shade(cameraRay.getRay(), object.collide(cameraRay.getRay())).getColor());
            }
        };
        pool.execute(run);
        buffer.clear();
    }
}
Iterator it=camera.Iterator();
列表缓冲区=新的ArrayList(1000);
while(it.hasNext()){
add(it.next());
if(buffer.size()>=1000 | |!it.hasNext()){
可运行运行运行=()->{
用于(T摄影机阵列:缓冲区){
if(object.collide(cameraRay.getRay())==null)//无冲突
地图放置(照相机,背景颜色);
其他的
map.put(cameraRay,this.shader.shade(cameraRay.getRay(),object.collide(cameraRay.getRay()).getColor());
}
};
pool.execute(run);
buffer.clear();
}
}

但非常奇怪的是,
Runnable
块现在从未被输入,为什么?

让我困惑的是,您的Runnable都使用相同的迭代器。更让我惊讶的是,您在迭代迭代器时生成了runnables,但这些runnables也操纵迭代器。这段代码可能(并且将会,正如你的问题所证明的那样)导致一系列的竞争条件和随之而来的头痛问题

我建议如下:

507
169
86624
625
626
Exception in thread "pool-2-thread-2" java.lang.OutOfMemoryError: Java heap space
Exception in thread "pool-2-thread-3" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(Unknown Source)
    at com.sun.javafx.application.LauncherImpl.launchApplication(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.util.concurrent.ThreadPoolExecutor.addWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
    at raytracer.impl.ParallelRenderer.render(ParallelRenderer.java:78)
    at raytracer.ImageViewer.main(ImageViewer.java:118)
    ... 11 more
Exception in thread "pool-2-thread-4" java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: Java heap space
    at raytracer.impl.TriangleImpl.collide(TriangleImpl.java:87)
    at raytracer.impl.SimpleScene.collide(SimpleScene.java:27)
    at raytracer.impl.ParallelRenderer.lambda$0(ParallelRenderer.java:71)
    at raytracer.impl.ParallelRenderer$$Lambda$48/24559708.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
  • 获取摄影机迭代器
  • 空缓冲区
  • 将迭代器中的第一个x(例如1000)样本读入缓冲区
  • 使用缓冲区创建一个runnable,它将对其1000个条目执行一些操作
  • 将runnable提交给服务并返回到2。重复此操作,直到迭代器不再具有next
  • 假设您对数据的处理(明显)比在相机上迭代一次要慢,这应该可以做到。如果不是这样,那么就没有理由使用多线程

    更新2

    我已将我的代码示例更新为可以工作的代码:

    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
        ExecutorService pool = Executors.newFixedThreadPool(4);
        Iterator<Integer> it = getIt();
        Task t = new Task(map);
        while (it.hasNext()) {
            t.add(it.next());
            if (t.size()>=1000 || !it.hasNext()) {
                pool.submit(t);
                t = new Task(map);
            }
        }
        pool.shutdown();
        pool.awaitTermination(1, TimeUnit.DAYS);
    
        // Breakpoint here to inspect map
        System.out.println("Done!");
    }
    
    publicstaticvoidmain(String[]args)抛出InterruptedException{
    ConcurrentHashMap=新的ConcurrentHashMap();
    ExecutorService池=Executors.newFixedThreadPool(4);
    迭代器it=getIt();
    任务t=新任务(map);
    while(it.hasNext()){
    t、 添加(it.next());
    如果(t.size()>=1000 | |!it.hasNext()){
    提交(t);
    t=新任务(map);
    }
    }
    pool.shutdown();
    池终止(1,时间单位:天);
    //此处的断点用于检查映射
    System.out.println(“完成!”);
    }
    

    private静态迭代器getIt(){
    返回新的迭代器(){
    私有整数nr=0;
    @凌驾
    公共布尔hasNext(){
    返回nr<20000;
    }
    @凌驾
    公共整数next(){
    返回nr++;
    }
    };
    }
    

    私有静态类任务扩展ArrayList实现可运行{
    私有最终ConcurrentHashMap映射;
    公共任务(ConcurrentHashMap映射){
    this.map=map;
    }
    @凌驾
    公开募捐{
    试一试{
    for(整数i:此){
    //模拟一些压缩:每个数字在10次迭代中写入stdout:每个Runnable需要10000次打印
    对于(int j=0;j<10;j++){
    System.out.println(“迭代”+j+”表示“+i”);
    }
    //在映射中存储一些东西,即这个整数,在您的例子中是T,已经被处理过了
    地图。放置(i,“完成”);
    }
    }捕获(例外e){
    e、 printStackTrace();
    }
    }
    }
    
    大约20-30秒后到达断点,映射包含与字符串“Done”配对的所有整数


    迭代器的next()方法是否同步?您认为是否有可能使用java 8流?n2()不同步,因为这是一个决定方法签名的练习。而且它还明确声称使用ExecutorService…T的类型是什么?变量名为
    cameraRay
    。它是一个大物体还是一个很小的物体?它是否正确实现了
    hashCode()
    equals()
    ?@Magnamag它非常小,由两个3d向量和两个像素坐标整数组成。我想
    hashCode()
    equals()
    可以正常工作,因为使用顺序渲染器一切都很好。@Rafael有两个3d向量+坐标。。。这最多是8个整数。每个
    int
    需要
    4
    字节,因此每个T至少是
    32
    字节。如果您有
    1024*768=786432
    Ts,则只有地图的键最多占用
    ~25 mb
    。我不是说你应该因为25MB的内存而耗尽内存,只是试着理解数字。颜色怎么样?它是大的还是小的?我做了一个编辑试图实现你的建议。注意:我无法创建t的通用数组(
    t[]buffer=new t[1000];
    )啊,对了,您无法创建通用数组。尝试使用任何集合,例如
    ArrayList
    作为缓冲区<代码>ArrayList缓冲区=新建ArrayList()。我在您的代码片段中看到,您对所有可运行程序重用了缓冲区。不要!为提交的每个runnable创建一个新的ArrayList。否则,您将在所有线程之间有效地共享一个集合。事实上,大多数代码看起来都很奇怪。我会编一个例子并更新我的答案。谢谢你的努力。你的例子也不起作用。最后,当执行所有任务时,
    map.size()
    为0。此外,您的帮助类需要位于方法(名为
    render(object,camera,image)
    )中,并且缺少泛型类型(
    Task
    )。但考虑到所有这些,每次都是一张空地图和
    等待
    
    private static Iterator<Integer> getIt(){
        return new Iterator<Integer>() {
    
            private int nr = 0;
    
            @Override
            public boolean hasNext() {
                return nr < 20000;
            }
    
            @Override
            public Integer next() {
                return nr++;
            }
        };
    }
    
    private static class Task extends ArrayList<Integer> implements Runnable{
        private final ConcurrentHashMap<Integer, String> map;
    
        public Task(ConcurrentHashMap<Integer, String> map) {
            this.map = map;
        }
    
        @Override
        public void run() {
            try{
                for (Integer i : this) {
                    // Simulate some crunching: write to the stdout in 10 iterations for each number: 10 000 prints for each Runnable
                    for (int j = 0; j < 10; j++) {
                        System.out.println("Iteration "+j+" for "+i);
                    }
                    // Store something in the map, namely that this Integer, or T in your case, has been processed
                    map.put(i, "Done");
                }
            } catch(Exception e){
                e.printStackTrace();
            }
        }
    }