将两个不同的方法彼此同步或同步变量

Synchronize two different methods with eachother or synchronize variables

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

您可以将方法声明为同步:

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

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

Synchronized Methods

如果您创建方法 synchronized,那么在任何给定时间都只允许在对象上执行一个方法。如果这个 class 有多个实例,并且它们不是静态的,那么每个实例都可以执行一个方法。在下面的例子中,如果同时调用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 方法相同,因为某些其他线程可能会在对 getA 的调用之间调用 foogetB.

一种方法是让另一个 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;
    }
}

还有更多优化的锁定方案可用,但既然你问的一般,以上就足够了。

你可以像这样使整个线程安全:

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,即使 "only-one-thread-at-a-time" 行为足以实现其中一种方法方法。这是因为 Java Memory Model,一旦您在 Java.

中了解了有关多线程的所有其他内容,就应该立即查看该主题

到目前为止你有四个答案,都告诉你同一件事:同步两种方法。但问题是:即使 同步,您仍然永远无法确定结果会是什么。在调用 add() 的线程和调用 foo() 的线程之间存在数据竞争。由 add() 编辑的答案 return 将取决于它是赢得比赛还是输掉比赛。

事实上,在这个特定示例中,同步根本没有增加任何价值。

通过同步,可以保证 foo() 方法调用和 add() 方法调用不会重叠。您可以使用这些知识来证明 add() 将 return 20(如果它输掉比赛)或 30 如果它赢得比赛。

但事实是,在这个特定示例中,由于 JVM 的工作方式,add() 总是 returned 为 20 或 30。

如果计算有更多的步骤,特别是如果它循环并多次引用 b,那么同步就很重要了。有了同步就只有两个可能的答案,但没有同步,其他结果也是可能的。