【问题标题】:Java issues with notify() / wait()通知()/等待()的Java问题
【发布时间】:2013-08-12 07:09:49
【问题描述】:

我在Iterator 实例中有一个生成器,每次在迭代器上调用next() 时,它都会生成并返回一个项目。它似乎确实有效,但我得到了 null 值返回。

类代码:

package spiral;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author student
 */
public class Spiral7DGenerator implements Iterator<List<Integer>> {
    private boolean releaseNext = false;
    private List<Integer> releaseList;

    public Spiral7DGenerator() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int t = 0; true; t++) {
                    loops(t);
                }
            }
        }).start();
    }

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public List<Integer> next() {
        synchronized(this) {
            releaseNext = true;
            notify();
            return releaseList;
        }
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    private void loops(int t) {
        for (int d1 = 0; d1 <= t; d1++) {
            for (int d2 = 0; d2 <= (t - d1); d2++) {
                for (int d3 = 0; d3 <= (t - d1 - d2); d3++) {
                    for (int d4 = 0; d4 <= (t - d1 - d2 - d3); d4++) {
                        for (int d5 = 0; d5 <= (t - d1 - d2 - d3 - d4); d5++) {
                            for (int d6 = 0; d6 <= (t - d1 - d2 - d3 - d4 - d5); d6++) {
                                int d7 = (t - d1 - d2 - d3 - d4 - d5 - d6);
                                generate(0, d1, d2, d3, d4, d5, d6, d7);
                            }
                        }
                    }
                }
            }
        }
    }

    private void generate(int pos, Integer... array) {
        if (pos == array.length) {
            List<Integer> list = new ArrayList<>();
            list.addAll(Arrays.asList(array));
            synchronized(this) {
                while (!releaseNext) {
                    try {
                        wait();
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Spiral7DGenerator.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
            releaseNext = false;
            releaseList = list;
            return;
        }
        generate(pos + 1, array);
        array[pos] = -array[pos];
        if (array[pos] != 0) {
            generate(pos + 1, array);
        }
    }
}

测试代码:

package spiral;

import org.junit.Test;
import static org.junit.Assert.*;

/**
 *
 * @author Beheerder
 */
public class Spiral7DGeneratorTest {

    public Spiral7DGeneratorTest() {
    }

    @Test
    public void testHasNext() {
    }

    @Test
    public void testNext() {
        System.out.println("test");
        Spiral7DGenerator s7dg = new Spiral7DGenerator();
        System.out.println("s7dg.next() = " + s7dg.next());
        System.out.println("s7dg.next() = " + s7dg.next());
        System.out.println("s7dg.next() = " + s7dg.next());
        System.out.println("s7dg.next() = " + s7dg.next());
    }

    @Test
    public void testRemove() {
    }

}

测试输出:

test
s7dg.next() = null
s7dg.next() = null
s7dg.next() = null
s7dg.next() = null

恐怕这只是一件简单的事情,但我完全忽略了它。

【问题讨论】:

  • 你真的需要写这么复杂的代码。对我来说,这很难读,也很难理解。我在阅读那些嵌套的 for 循环时内存不足。
  • @JunedAhsan 虽然问题本身与代码无关,但我可以解释为什么它如此复杂。那是因为一般来说制作螺旋很复杂,当然我更愿意让它通用而不是这个,但目前这对我来说太难了。
  • 您在 Spiral7DGenerator() const 中的循环永远不会结束,并且在每次迭代时都会启动新线程。这是一个错误(因为内存有限)
  • 使用多线程很少能简化代码。我仍然认为这更简单,更有可能工作stackoverflow.com/questions/18074982/…

标签: java wait notify


【解决方案1】:

我还没有完全理解您的代码,但我可以提出两点:

  • releaseList 必须声明为 volatile(至少),因为它是在同步块之外分配的。
  • 您的next 方法不会等待通知线程唤醒并产生结果。这是一个竞争条件。您需要next 等待额外的互斥锁(或其他东西)以允许生成器通知releaseList 已设置。 (实际上,当您这样做时,您将不再需要使 releaseList 变为 volatile。)

【讨论】:

  • 我怀疑是next() 根本不等待工作线程导致所有空值。你对releaseList(和releaseNext)也没有安全更新是完全正确的。
【解决方案2】:

这里的想法是工作线程将结果流提供给主线程,对吗?

我建议你不要尝试手动构建并发控制逻辑,而是使用SynchronousQueue。代码如下所示:

public class Spiral7DGenerator implements Iterator<List<Integer>> {
    private BlockingQueue<List<Integer>> spirals = new SynchronousQueue<List<Integer>>();

    @Override
    public List<Integer> next() {
        return spirals.take();
    }

    private void generate(int pos, Integer... array) {
        if (pos == array.length) {
            List<Integer> list = new ArrayList<>();
            list.addAll(Arrays.asList(array));
            spirals.put(list);
            return;
        }
    // etc
    }
}

【讨论】:

  • 谢谢!这正是我所需要的。虽然 Java 没有 Python 的 yield 等价物,但真是太可惜了。
  • 确实如此。当然,Scala 已经添加了可以让你构建诸如生成器之类的东西的延续。不过,我认为这不太可能出现在 Java 中。
  • 实际上,这可能不是我想要的 100%。我希望generate() 只在spirals.isEmpty() 时做某事。 BlockingQueue 是否可以(轻松)修改为在spirals.size() &gt; threshold 时阻塞?因为我希望它在spirals.size() &gt; 0 时阻止/等待。然后等到next()被调用,再生成下一个元素,以此类推
  • 事实上,spirals.put 将阻塞直到主线程出现并调用spirals.take,因此一次只会有一个值通过队列。如果您想对队列大小设置更大但仍然固定的限制,请将SynchronousQueue 替换为您需要的任何容量的ArrayBlockingQueue
  • 啊,好吧,SynchronousQueue 确实正是我所需要的,我应该在询问之前阅读文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 2013-12-24
  • 1970-01-01
相关资源
最近更新 更多