JVM似乎很快就停止了上下文切换

时间:2015-02-12 20:25:22

标签: java multithreading concurrency jvm

我正在实现生产者 - 消费者并发问题的天真版本。并且线程首先在非常快速之间切换,然后在i = 50周围停止。由于某种原因添加其他打印语句允许JVM上下文切换线程并完成程序。

为什么JVM上下文不切换线程以便程序完成?

// Producer - Consumer problem
// Producer constantly puts items into array, while consumer takes them out

class IntBuffer {
    private int[] buffer;
    private int index;

    public IntBuffer(int size) {
        buffer = new int[size];
        index = 0;
    }

    public void add(int item) {
        while (true) {
            if (index < buffer.length) {
                buffer[index] = item;
                index++;
                return;
            }
        }
    }

    public int remove() {
        while (true) {
            if (index > 0) {
                index--;
                int tmp = buffer[index];
                buffer[index] = 0;
                return tmp;
            }
        }
    }

    public void printState() {
        System.out.println("Index " + index);
        System.out.println("State " + this);
    }

    public String toString() {
        String res = "";
        for (int i = 0; i < buffer.length; i++) {
            res += buffer[i] + " ";
        }
        return res;
    }
}

class Producer extends Thread {
    private IntBuffer buffer;

    public Producer(IntBuffer buffer) {
        this.buffer = buffer;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("added " + i);
            buffer.add(i);
        }
    }
}

class Consumer extends Thread {
    private IntBuffer buffer;

    public Consumer(IntBuffer buffer) {
        this.buffer = buffer;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("removed " + i);
            buffer.remove();
        }
    }
}

public class Main {

    public static void main(String[] args) {
        IntBuffer buf = new IntBuffer(10);
        Thread t1 = new Thread(new Producer(buf));
        Thread t2 = new Thread(new Consumer(buf));
        t1.start();
        t2.start();
        System.out.println(buf);
    }
}

2 个答案:

答案 0 :(得分:4)

您的问题没有提供足够的详细信息来给出答案的信心(至少,这些额外的印刷语句去哪里都不清楚),所以我会在这里做一些(合理的)猜测。

  1. 您的代码不正确。 IntBuffer不是线程安全的,但可以从多个线程访问它。

  2. IntBuffer上的任何操作都不会建立before-before关系,因此一个线程所做的更改可能对另一个线程不可见。这就是为什么Producer线程可以“相信”缓冲区已满,而Consumer线程“认为”它是空的。在这种情况下,程序永远不会终止。

  3. 这两个陈述不是猜测,它们是基于Java内存模型的事实。这就是我的猜测为什么附加的打印语句可以解决它的问题:

    1. 在许多JVM实现中,println方法在内部使用同步。这就是为什么调用它会创建一个内存栅栏,并使一个线程中的更改对另一个线程可见,从而消除了2)中描述的问题。
    2. 但是,如果你真的想解决这个问题,你应该让IntBuffer线程安全。

答案 1 :(得分:1)

至少您需要volatilebuffer上的index关键字。其次,你需要在你拥有的ifs的真正手臂下访问index一次。即使在那之后,你将面临10点的界限访问,你将需要更多的修复来解决这个问题。你的缓冲区是事实上的堆栈。因此,即使在所有这些之后,您的remove()也可以使用陈旧索引,因此您将在堆栈中间删除。您可以使用0作为特殊值,标记已经处理的插槽为空。

有了这一切,我认为您的代码不易被挽救。它几乎需要使用适当的设施进行完全重写。我同意@kraskevich:

  

@StuartHa Naive usually means simple(and most likely inefficent) solution, not an incorrect one.