通知()/等待()的Java问题

Posted

技术标签:

【中文标题】通知()/等待()的Java问题【英文标题】:Java issues with notify() / wait() 【发布时间】: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 中的循环永远不会结束,并且在每次迭代时都会启动新线程。这是一个错误(因为内存有限) 使用多线程很少能简化代码。我仍然认为这更简单,更有可能工作***.com/questions/18074982/… 另一个答案***.com/questions/11570132/… 【参考方案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 确实正是我所需要的,我应该在询问之前阅读文档。

以上是关于通知()/等待()的Java问题的主要内容,如果未能解决你的问题,请参考以下文章

等待/通知的奇怪java行为

Day826.Java多线程等待&通知机制 -Java 并发编程实战

Day826.Java多线程等待&通知机制 -Java 并发编程实战

Java线程与并发编程实践----等待通知(生产者消费者问题)线程

Java中多线程等待通知示例中的线程连接(1)用法

Java并发之等待/通知机制