我不确定我是否应该在我的示例中同步方法methodOne()。我想不是,但我不是百分百肯定。你能告诉我该做什么吗?
public class SynchroIssue {
class Test {
private double a = 0;
void methodOne() {
a++;
}
void go() {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
methodOne();
System.out.println(Thread.currentThread().getName() + ", a = " + a);
}
}
}).start();
}
}
public static void main(String... args) {
SynchroIssue mainObj = new SynchroIssue();
SynchroIssue.Test object1 = mainObj.new Test();
SynchroIssue.Test object2 = mainObj.new Test();
object1.go();
object2.go();
}
}
答案 0 :(得分:4)
假设您实际上将同时使用SynchroIssue类的实例,而您当前没有这样做,答案是肯定的。
增量运算符不是原子的。它实际上是3条指令:
获取当前值。
添加1。
存储新值。
如果未同步,并发线程可能会重叠这些步骤,从而导致奇怪的行为。
另一个选择,如果你真的只对整数感兴趣,那就是使用AtomicInteger,它有方法以原子方式递增。
答案 1 :(得分:1)
object1和object2是不同的对象,每个只启动一个线程,变量“a”是私有的而不是静态的,所以“a”也是不同的对象,并且线程之间没有交互。所以不需要同步methodOne()。
答案 2 :(得分:1)
在这个具体示例中,通过同步方法没有任何价值,因为只有一个线程实际上与给定实例进行交互。
如果您两次致电object1.go()
,则会遇到问题。使用synchronized
不是解决该问题的最佳解决方案,但您应该使用java.util.concurrent.atomic.DoubleAccumulator
,尽管AtomicInteger
也可以正常运行,因为您从0开始并且只是递增1。
通常,您应该谨慎使用synchronized
来推送自己的同步协议。更喜欢已知的线程安全类,它们是可用的。 java.util.concurrent
是一个值得关注的好地方。
答案 3 :(得分:1)
你应该,但它不会解决你的问题。 如果要同步方法,则一次只能有一个线程增加变量。但是下面的System.out.println仍然可以打印另一个值,因为在你调用它时,另一个线程可能已经增加了a。 问题的解决方案是,methodOne还必须返回变量。像这样:
for (int i = 0; i < Integer.MAX_VALUE; i++) {
System.out.println(Thread.currentThread().getName() + ", a = " + methodOne());
}
线程应该:
xsi:schemaLocation
编辑:正如其他人已经指出的那样,如果你想让变量变为静态,你只需要这样做。否则,您可以保留代码原样。
答案 4 :(得分:0)
我想在 Brett Okken 的回答中添加一些提示:
大多数情况下,如果您的类中有一个成员变量,并且由多个线程在并发上下文中通过您的类的方法进行修改,那么您应该考虑其中一个同步范围。
始终寻求最小的可用同步范围。
希望这会有所帮助。