我有一个服务女巫可以通过不同的线程访问。该服务有3个long和两个方法 - 将它们全部加在一起的方法和将中间一个更改为0的方法。
private long a = 10;
private long b = 10;
private long c = 10;
public long add(){
return a + b + c;
}
public void foo(){
b = 0;
}
现在当然代码更复杂但重点是当线程正在访问add()方法时,另一个线程可以访问foo()方法并更改b的值,所以我永远不会确定结果是什么。我如何同步这两个方法或同步a,b,c变量,这将是线程安全的?
答案 0 :(得分:4)
如果您创建方法synchronized
,那么在任何给定时间只允许一个对象执行。如果您有多个此类的实例,并且它们不是静态的,则每个实例都可以执行一个方法。在以下示例中,如果同时调用add
和foo
,则其中一个将等待:
public synchronized long add() {
return a + b + c;
}
public synchronized void foo() {
b = 0;
}
注意:同步不会构成。也就是说,组合两个synchronized
的东西与组合两个东西然后添加synchronized
不同。假设您拥有synchronized
方法getA
,getB
,getC
,那么:
public long add2() {
return getA() + getB() + getC();
}
将 与旧的add
方法完全相同,因为某些其他线程可能会在对foo
和{{1}的调用之间调用getA
}。
答案 1 :(得分:2)
您可以将方法声明为synchronized:
public synchronized long add(){
return a + b + c;
}
public synchronized void foo(){
b = 0;
}
答案 2 :(得分:2)
这样做的方法是让另一个object
说
private object elock = new byte[0];
然后在两个方法的每一个中用
包围临界区synchronized(elock) {/* critical section */}
在
中public long add(){
synchronized(elock) {
return a + b + c;
}
}
public void foo(){
synchronized(elock) {
b = 0;
}
}
可以使用更优化的锁定方案,但由于您通常会问,上述内容应该足够了。
答案 3 :(得分:2)
到目前为止,你有四个答案,都告诉你同样的事情:同步两种方法。但事情就是这样:即使与进行同步,你仍然无法确定结果是什么。你在调用add()的线程和调用foo()的线程之间有数据竞争。 add()返回的答案将取决于它是赢还是输。
实际上,在这个特定示例中,同步根本不会添加任何值。
通过同步,可以保证foo()方法调用和add()方法调用不会重叠。您可以使用该知识来证明add()将返回20(如果它失去了比赛)或30如果它赢得比赛。
但事实是,在这个特定的例子中,由于JVM的工作方式,add()总是会返回20或30.
如果计算有更多步骤,特别是如果它循环并且多次引用b,那么同步就很重要了。通过同步,只有两个可能的答案,但没有同步,其他结果也是可能的。
答案 4 :(得分:1)
你可以像这样使整个事情线程安全:
private long a = 10;
private long b = 10;
private long c = 10;
public synchronized long add(){
return a + b + c;
}
public synchronized void foo(){
b = 0;
}
将非静态方法标记为synchronized
具有以下效果:
public synchronized void a() {
// do something
}
// is the same as:
public void a() {
synchronized(this) {
// do something
}
}
并且由于没有两个线程可以同时锁定同一个对象(在这种情况下对象是this
),如果一个线程调用a()
,则另一个线程调用b()
},第二个线程必须等到第一个线程离开a()
。
您可以详细了解此主题here。
请注意,在您的示例中,不足以将一个方法声明为synchronized
,即使“一次只有一个线程”行为足够其中一种方法。这是因为Java Memory Model,一旦你了解了Java中的多线程的所有其他内容,你就应该看一下这个主题。