Java中同一对象的不同实例上的多线程

时间:2010-06-23 03:37:58

标签: java multithreading synchronization race-condition

我已经知道每个类字节码都已经为每个类加载器加载到内存中一次,因此当一个线程正在执行某个方法的字节代码时,另一个线程会出现?

1 主题 - > 1 实例 - Foo ==没问题。

X 主题 - > 1 实例 - Foo ==需要处理这一点很清楚。

X 主题 - > X各自的实例 - Foo == ????

我应该确保方法中没有搞砸了吗? 如果该方法使用实例级别变量,我可以确定它将使用正确的变量吗?

更新

我看到我的问题并不清楚,这是一个带数字的例子

我有一个类型 Foo 的对象没有同步!!

我有5个 Foo 实例,其中每个实例都有5个线程运行,并且访问实例级参数,例如:

class FOO {
     private SomeObject someObject=new SomeObject();

     private void problematicMethod(Data data) {
         someObject.doSomethingWithTheData(data);
         data.doSomethingWithSomeObject(someObject); 
// any way you want it use the data or export the data
     }
}

我问这里有问题,因为这个类只有 1 字节代码,而且此对象的 5 实例访问这个字节代码,所以如果我想阻止它们在相同的字节码上重叠,我该怎么办?

谢谢, 亚当。

5 个答案:

答案 0 :(得分:11)

  

我已经知道每个类字节码都已经为每个类加载器加载到内存中一次,因此当一个线程正在执行某个方法的字节代码时,另一个线程会出现?

类加载和字节代码在这里无关紧要。字节代码是一组类似于汇编的指令,JVM将其解释并编译为本机机器代码。多个线程可以安全地遵循编码到字节代码中的指令集。

  

1个主题 - > 1个实例 - 类Test,没问题

大部分都是正确的。如果只有一个Thread,那么就没有任何线程安全的立即需要。但是,忽略线程安全会限制增长和可重用性。

  

X个线程 - > 1个实例 - 类Test,需要处理这个很清楚。

嗯,是的,出于线程可见性原因并确保关键区域以原子方式执行,使用同步或锁定技术非常重要。当然,这一切都取决于对吗?!如果你的类没有状态(实例或类变量),那么你真的不需要让它具有线程安全性(想想像Java的ExecutorsArrays,{{1}这样的实用程序类}))。

  

X个线程 - > X各自的实例 - 类Test,????

如果每个线程都有自己的Test类实例,并且在多个线程中没有共享单个实例,那么这与第一个示例相同。如果两个或多个线程引用了Test实例,那么这与第二个示例相同。

  

如果方法使用类级别变量,我可以确定它会使用正确的变量吗?

类变量Java中的Collections变量。已经发布了两个答案,强调了使用static来防止多个线程同时修改类变量的重要性。未提及使用synchronizedsynchronized来确保您没有看到类变量的陈旧版本的重要性。

答案 1 :(得分:3)

您需要在共享资源上进行同步。如果该资源是类级别字段,则应该在类本身上进行同步:

public class Foo {
  private static int someNumber = 0;
  // not thread safe
  public void inc_unsafe()  { 
    someNumber++;
  }

  // not thread safe either; we are sync'ing here on an INSTANCE of
  // the Foo class
  public synchronized void inc_also_unsafe()  { 
    someNumber++;
  }

  // here we are safe, because for static methods, synchronized will use the class
  // itself
  public static synchronized void inc_safe()  { 
    someNumber++;
  }

  // also safe, since we use the class itself
  public static synchronized void inc_also_safe()  { 
    synchronized (Foo.class) {
      someNumber++;
    }
  }
}

请注意,{{java.util.concurrent}}提供了比Java的{{synchronized}}关键字更多的保护共享数据的方法。看看它,因为它可能是你想要的。

对于任何想要声称int递增已经是线程安全的人,请注意这只是一个示例,基本概念可以应用于任何事情。

答案 2 :(得分:2)

添加到dave的answer,如果有多个静态方法可以处理不同的静态成员,最好在不同的静态对象上进行同步。

public class Foo {
  private static int someNumber = 0;
  private static int otherNumber = 0;
  private static final Object lock1 = new Object();
  private static final Object lock2 = new Object();

  public static void incSomeNumber() {
    synchronized (lock1) {
      someNumber++;
    }
  }

  public static void incOtherNumber() {
    synchronized (lock2) {
      otherNumber++;
    }
  }
}

通过这种方式,两个不同的线程可以同时调用incSomeNumberincOtherNumber,而不会卡在同步上。


修改

以下是与AtomicInterger相同的示例。请注意,不需要显式锁定。 AtomicInterger上的所有操作都是原子操作,并使用硬件操作实现。因此,它们可以带来更好的性能。

import java.util.concurrent.atomic.AtomicInteger;

public class Foo {
  private static AtomicInteger someNumber = new AtomicInteger(0);
  private static AtomicInteger otherNumber = new AtomicInteger(0);

  public static int incSomeNumber() {
    return someNumber.incrementAndGet();
  }

  public static int incOtherNumber() {
    return otherNumber.incrementAndGet();
  }
}

答案 3 :(得分:0)

所有线程都应该转到同一个类加载器。使用FOo.class的10个线程,所有10个线程都具有相同的确切对象。在不同的类加载器中获得相同类的唯一方法是

a)你编写了自己的代码,用于实现类加载器魔法 b)你做了一些奇怪的事情,比如在战争中包含你的代码,在共享的tomcat lib文件夹里面......并且做了正确的事件序列,以便从不同的地方加载2个副本。

在所有正常情况下..你创建一个类,只有1个类对象的副本,并且(this)上的所有同步都将遍及整个应用程序。

答案 4 :(得分:0)

我认为,实例变量作为局部变量对每个线程都是唯一的。所以默认情况下它是线程安全的。但是,它仍然需要由同步来处理。