由于所有Java应用程序最终都由JVM运行,为什么JVM不能在运行时将单线程代码包装成多线程代码,具体取决于运行/访问部分代码的线程数量。 JVM肯定知道正在运行的线程数,它确实知道哪些类是线程,哪些代码可以被多个线程访问。
无法实施的原因是什么,或者是什么导致这种情况复杂化?
答案 0 :(得分:5)
在多个线程使用的任何内容上简单地喷涂synchronized / volatile / Lock不会导致正确的多线程行为。例如,运行时如何知道锁的正确粒度?它会如何避免死锁?
早期的集合类,例如:Vector
和Hashtable
的设计具有类似的天真并发视图。一切都是同步的。事实证明,你仍然可以很容易地陷入困境。例如,假设您要检查Vector是否包含至少一个元素,如果是,那么您将删除一个元素。每个对Vector的调用都会被同步,但是另一个线程可以在这些调用之间执行,因此你最终可能会遇到竞争条件错误。 (当我之前提到锁的粒度时,这就是我所指的。)
答案 1 :(得分:1)
自动添加同步通常不会产生积极影响。同步成本,性能和内存。性能,因为处理器必须检查底层锁。和内存,因为锁必须存储在某个地方。当运行时在任何地方添加锁时,程序将运行单线程(因为每个方法一次只能从一个线程访问),但现在CPU的成本更高,内存负载更多(因为锁处理)。 / p>
通常,Java运行时没有足够的信息以巧妙的方式添加锁。但它恰恰相反:使用所谓的“escape analysis”,它可以检查内存块是否永远不会转义某个代码块(并且永远不会共享给另一个线程)。如果是这种情况,则应用多个优化。其中之一是VM删除了该块的所有同步。
有些系统拥有足够的信息来自动应用锁:数据库管理系统。更复杂的数据库引擎使用称为“multi version concurrency”的技术。使用这种技术,人们只需要锁定来写入数据,而不需要读取数据。因此,与传统方法一样,需要更少的锁,并且可以并行运行更多代码。但这带来了成本:有时并行度变高,系统处于不一致状态。然后系统撤消一些更改并在以后重复它们。
这种方法可以(在某种程度上)自动方式提供给JVM。然后将其称为“software transactional memory”。这非常接近你的自动锁定的想法,并留下足够的并行空间是有用的。在JVM上,语言Clojure使用软件事务存储器。
因此,虽然JVM一般不能自动添加锁,但Clojure在一定程度上实现了这一点。试一试,看看它有多好。
答案 2 :(得分:0)
我可以考虑以下原因: 应用程序可能使用静态变量,因此部分共享它们正在使用的类的应用程序可能会通过更改共享状态来互相打扰。
您真正想要的是由Java EE容器实现,该容器正在运行多个应用程序。看来你在建议JSE容器(我不知道这个术语是否存在)。尝试向Oracle推荐它。它可能是一个很酷的JSR!