我正在尝试使用两个线程的代码,一个线程递增一个共享的长变量,另一个线程递减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()方法之后,这将产生零作为输出。
答案 0 :(得分:1)
如果多个线程可以访问某个字段,则需要synchronize。
答案 1 :(得分:1)
正如其他人所指出的,如果没有incr()
,您的dec()
和synchronize
方法就不是线程安全的。
添加System.out.println
可能只是因为它改变了时间而改变了行为。此外,隐藏在此调用中的是同步,充当ersatz synchronize
。这比没有同步更好。这是一系列可能的事件:
因此,由于打印具有同步块,因此它允许两个线程交替,哪个线程正在更改变量以及哪个线程正在打印。偶尔,时间将会错过,竞争条件将发生,并且计数将不同步。
您在多核系统上可能遇到的另一个问题是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;}
}