同时使两个不同的方法同步或同步变量

时间:2015-02-10 18:36:33

标签: java multithreading concurrency

我有一个服务女巫可以通过不同的线程访问。该服务有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变量,这将是线程安全的?

5 个答案:

答案 0 :(得分:4)

如果您创建方法synchronized,那么在任何给定时间只允许一个对象执行。如果您有多个此类的实例,并且它们不是静态的,则每个实例都可以执行一个方法。在以下示例中,如果同时调用addfoo,则其中一个将等待:

public synchronized long add() {
    return a + b + c;
}

public synchronized void foo() {
    b = 0;
}

注意:同步不会构成。也就是说,组合两个synchronized的东西与组合两个东西然后添加synchronized不同。假设您拥有synchronized方法getAgetBgetC,那么:

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;
}

请参阅Synchronized Methods

答案 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中的多线程的所有其他内容,你就应该看一下这个主题。