我已经知道每个类字节码都已经为每个类加载器加载到内存中一次,因此当一个线程正在执行某个方法的字节代码时,另一个线程会出现?
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 实例访问这个字节代码,所以如果我想阻止它们在相同的字节码上重叠,我该怎么办?
谢谢, 亚当。
答案 0 :(得分:11)
我已经知道每个类字节码都已经为每个类加载器加载到内存中一次,因此当一个线程正在执行某个方法的字节代码时,另一个线程会出现?
类加载和字节代码在这里无关紧要。字节代码是一组类似于汇编的指令,JVM将其解释并编译为本机机器代码。多个线程可以安全地遵循编码到字节代码中的指令集。
1个主题 - > 1个实例 - 类Test,没问题
大部分都是正确的。如果只有一个Thread,那么就没有任何线程安全的立即需要。但是,忽略线程安全会限制增长和可重用性。
X个线程 - > 1个实例 - 类Test,需要处理这个很清楚。
嗯,是的,出于线程可见性原因并确保关键区域以原子方式执行,使用同步或锁定技术非常重要。当然,这一切都取决于对吗?!如果你的类没有状态(实例或类变量),那么你真的不需要让它具有线程安全性(想想像Java的Executors
,Arrays
,{{1}这样的实用程序类}))。
X个线程 - > X各自的实例 - 类Test,????
如果每个线程都有自己的Test类实例,并且在多个线程中没有共享单个实例,那么这与第一个示例相同。如果两个或多个线程引用了Test实例,那么这与第二个示例相同。
如果方法使用类级别变量,我可以确定它会使用正确的变量吗?
类变量Java中的Collections
变量。已经发布了两个答案,强调了使用static
来防止多个线程同时修改类变量的重要性。未提及使用synchronized
或synchronized
来确保您没有看到类变量的陈旧版本的重要性。
答案 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++;
}
}
}
通过这种方式,两个不同的线程可以同时调用incSomeNumber
和incOtherNumber
,而不会卡在同步上。
修改强>
以下是与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)
我认为,实例变量作为局部变量对每个线程都是唯一的。所以默认情况下它是线程安全的。但是,它仍然需要由同步来处理。