14

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

Foo类的1 个线程 -> 1 个实例 == 没问题。

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

X个线程 -> X 个相应的实例 - Foo == ????

我应该确保方法中没有任何问题吗?如果该方法使用实例级变量,我可以确定它会使用正确的变量吗?

更新:

我看到我的问题有些人不清楚,这里有一个数字示例

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

我有该Foo的 5 个实例,每个实例中运行 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个实例访问这个字节码,所以如果我想防止它们在同一个字节码上重叠,我应该怎么做做?

谢谢,亚当。

4

5 回答 5

14

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

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

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

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

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

嗯,是的,出于线程可见性的原因并确保关键区域以原子方式执行,使用同步或锁定技术相当重要。当然,这一切都取决于对吧?!如果您的类没有状态(实例或类变量),那么您实际上不需要使其成为线程安全的(想想像 Java 的Executors, Arrays,类这样的实用程序Collections类)。

X个线程-> X个相应的实例-Test类,????

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

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

类变量是staticJava 中的变量。已经发布了两个答案,强调了使用synchronized以防止多个线程同时修改类变量的重要性。没有提到使用synchronizedvolatile确保您看不到类变量的陈旧版本的重要性。

于 2010-06-23T04:54:55.577 回答
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 递增已经是线程安全的人,请注意这只是一个示例,基本概念可以应用于任何事情。

于 2010-06-23T03:45:36.567 回答
2

添加到 dave 的答案中,如果您有多个适用于不同静态成员的静态方法,最好在单独的静态对象上同步。

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

这样,两个不同的线程可以同时调用incSomeNumberandincOtherNumber而不会卡在同步上。


编辑

这是与 相同的示例AtomicInterger。请注意,不需要显式锁定。s上的所有操作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();
  }
}
于 2010-06-23T03:54:52.580 回答
0

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

a)您编写了自己的代码来执行类加载器魔术
b)您做了一些奇怪的事情,例如在战争中和共享的 tomcat lib 文件夹中都包含您的代码……并执行了正确的事件序列以导致 2 个副本从不同的地方加载。

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

于 2010-06-23T04:03:19.380 回答
0

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

于 2013-08-31T11:55:10.500 回答