java线程:线程的异常行为

时间:2013-03-28 12:29:22

标签: java multithreading

我正在尝试使用两个线程的代码,一个线程递增一个共享的长变量,另一个线程递减var。

class Shared {
    private long a;
    public void incr() {
        a++;
    }
    public void dec() { a--; }
    public long getA(){return a;}
}

我将这个共享对象传递给两个Threads。 Nitems在每个线程中递增或递减的次数

Shared obj = new Shared();
Incrementer incrementer = new Incrementer(obj, nitems);
Decrementer decrementer = new Decrementer(obj , nitems);

递减线程的run方法: -

public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.dec();
    }
}

增量线程的run方法: -

public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.incr();

    }
}

当我运行它。我可以清楚地看到问题。运行整码20次后,结果不为零。现在好了,像这样改变了相同的run方法

//increment
    public void run()
    {
        for(int i=0; i<nitems; ++i)
        {
            s.incr();
            System.out.println("ghijk");

        }
    }

//decrement
    public void run()
{
    for(int i=0; i<nitems; ++i)
    {
        s.dec();
        System.out.println("abcdef");
    }
}

除了值不为零的一两次外,几乎所有时间的结果都为零。我的问题是什么时候引入这个SOP使这段代码正常工作?我认为只有在同步incr()和dec()方法之后,这将产生零作为输出。

3 个答案:

答案 0 :(得分:1)

如果多个线程可以访问某个字段,则需要synchronize

答案 1 :(得分:1)

正如其他人所指出的,如果没有incr(),您的dec()synchronize方法就不是线程安全的。

添加System.out.println可能只是因为它改变了时间而改变了行为。此外,隐藏在此调用中的是同步,充当ersatz synchronize。这比没有同步更好。这是一系列可能的事件:

  1. Thread-1 将计数器增加1(首先启动,然后先行)
  2. 线程-1 打印。
  3. Thread-1 正忙着打印时, Thread-2 会减少计数器。
  4. 线程-2 完成减量(在线程-1 完成打印之前)。
  5. Thread-2 进入打印状态,但必须等待,因为 Thread-1 锁定了输出流。
  6. Thread-2 正忙着打印时, Thread-1 会循环回到第1步。
  7. 因此,由于打印具有同步块,因此它允许两个线程交替,哪个线程正在更改变量以及哪个线程正在打印。偶尔,时间将会错过,竞争条件将发生,并且计数将不同步。

    您在多核系统上可能遇到的另一个问题是Java线程可以维护变量值的本地版本,因此每个线程都可以看到自己的副本。为防止这种情况,您需要声明变量volatile。遇到synchronize块时,非易失性变量将在线程间同步,因此volatile不是您需要的。 (println的另一个副作用可能是确保a在两个线程之间同步。)

    为简单起见,请使用并发包中的AtomicLong

答案 2 :(得分:0)

同步访问更改a

的方法
class Shared {
    private long a;
    public synchronized void incr() {
        a++;
    }
    public synchronized void dec() {
        a--;
    }
    public long getA(){return a;}
}