Java实例成员和并发性
根据我对java并发性的理解,似乎只有当线程访问给定对象(如servlet)的同一实例时,才能对实例成员的共享访问进行编码,以处理多线程访问。请参见此处: 由于并非所有应用程序都是基于servlet的,您如何确定哪些对象需要容纳多线程访问?例如,在一个大型的、非基于servlet的企业应用程序中,考虑到类的绝对数量,您如何从设计的角度确定哪些对象在运行时在多个线程之间只共享一个实例?我能想到的唯一情况是单身Java实例成员和并发性,java,multithreading,concurrency,Java,Multithreading,Concurrency,根据我对java并发性的理解,似乎只有当线程访问给定对象(如servlet)的同一实例时,才能对实例成员的共享访问进行编码,以处理多线程访问。请参见此处: 由于并非所有应用程序都是基于servlet的,您如何确定哪些对象需要容纳多线程访问?例如,在一个大型的、非基于servlet的企业应用程序中,考虑到类的绝对数量,您如何从设计的角度确定哪些对象在运行时在多个线程之间只共享一个实例?我能想到的唯一情况是单身 在Java的EL API中,javax.EL.BeanELResolver有一个私有内
在Java的EL API中,javax.EL.BeanELResolver有一个私有内部类,它使用同步来序列化对其成员之一的访问。除非我遗漏了什么,否则BeanELResolver看起来不像是一个单线程,因此每个线程都应该有自己的BeanELResolver实例。同步其中一个成员的设计考虑因素是什么?任何实例都可以在线程之间共享,而不仅仅是单实例 这就是为什么要设计一个开发团队中的任何人都可以立即看到哪些类型或实例将在线程之间共享,哪些不共享的设计非常困难。完全不可能阻止共享某些实例。因此,解决方案必须在其他地方。请仔细阅读“”以了解详细信息 同步用于两个目的:
由于无法阻止人们在不同线程之间共享单个
BeanELResolver
实例,因此他们可能需要确保并发访问不会破坏某些复杂的结构(可能是Map
)。在许多情况下,一个类的状态可以在多个线程之间共享,不仅仅是单身。例如,您可以让一个类或方法创建对象(某种工厂),并在所有创建的对象中注入相同的依赖项。注入的依赖项将在调用工厂方法的所有线程之间共享。依赖项可以是任何东西:计数器、数据库访问类等
例如:
class ThreadSafeCounter{
/* constructor omitted */
private final String name;
private final AtomicInteger i = new AtomicInteger();
int increment() { return i.incrementAndGet(); }
}
class SheepTracker {
public SheepTracker(ThreadSafeCounter c) { sheepCounter = c;}
private final ThreadSafeCounter sheepCounter;
public int addSheep() { return c.increment(); }
}
class SheepTrackerFactory {
private final ThreadSafeCounter c;
public SheepTracker newSheepAdder() {
return new SheepTracker(c);
}
}
在上面的例子中,
SheepTrackerFactory
可以被许多线程使用,这些线程都需要做同样的事情,即跟踪绵羊。所有线程中的sheep数都保存在一个全局状态变量中,ThreadSafeCounter
(在本例中,它可能只是一个AtomicInteger
,但请注意,您可以想象该类如何包含其他状态/操作)。现在,每个SheepTracker
都可以是一个轻量级类,它可以执行其他不需要同步的操作,但是当它们需要增加羊的数量时,它们会以线程安全的方式来执行。您提出了一个非常广泛的问题,因此我将尝试用广泛的答案来回答。在跳入类之前,你的设计必须考虑的第一件事就是应用程序线程的设计。在这个步骤中,你考虑手头的任务,以及如何最好地利用必须解决的硬件。基于此,您可以为应用程序选择最佳的线程设计
例如,应用程序是否执行密集的计算?如果是这样的话,部分计算可以并行化以更好地利用多核CPU吗?如果是这样,请确保在不同的内核上并行设计多个线程
您的应用程序是否执行大量I/O操作?如果是这样,最好将它们并行化,这样多个线程就可以处理输入/输出(这很慢,需要大量等待外部设备),而其他线程继续处理自己的任务。这就是为什么servlet由多个线程并行执行的原因
一旦您决定了要并行化的任务和希望在单个线程中执行的任务,您就可以进入类本身的设计了。现在,您的软件的哪些部分必须是线程安全的,哪些部分不是。您有一个由负责I/O的线程池访问的数据结构吗?它必须是线程安全的。您有一个由执行维护任务的单个线程访问的对象吗?不一定是这样
不管怎么说,这与单身汉无关。Singlton是一种设计模式,意味着只能创建特定对象的单个实例。它没有说明访问它或其成员的线程数。同步是一种安全措施。如果实例的引用转义,则应该在多线程环境中使用它。在编写类之前,应该知道实例是否将同时使用。这是程序员在impl中必须考虑的应用程序设计选择。这是一个复杂的主题-最好的建议是阅读Brian Goetz的Java并发性实践-您需要知道的一切都在这里。没有任何
String
可以是单例。根据定义,单例是其类的唯一实例。即使在最简单的Java程序中,也有成千上万个不同的String
实例。(例如,每个类名、字段名和方法名都是一个字符串。)@jameslarge:我提到了String
,因为有些人把不可变或值类型与单例混为一谈。通常,即使是public final static String
常量也不是单例(javac
如果您从外部类引用它,例如在注释中引用它,就会创建一个副本)。我认为我们对“单例”的含义并不一致。如果我有一个方便的副本,我会选择“设计模式”的定义,但我很确定维基百科的定义是等效的:根据这个定义,说它没有意义