[英]Java Thread - Synchronization issue
来自Sun的教程:
同步方法启用了一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。 (一个重要的例外:构造对象后无法修改的最终字段,一旦构造了对象,就可以通过非同步方法安全地读取)这种策略是有效的,但是可能会带来活性问题,因为我们将会请参阅本课后面的内容。
Q1。 上述语句是否意味着如果一个类的对象将在多个线程之间共享,那么该类的所有实例方法 (除了最终字段的getter)都应该同步,因为实例方法处理实例变量?
为了理解Java中的并发性,我推荐了非常有价值的Java Concurrency in Practice 。
在回答您的具体问题时,尽管同步所有方法是实现线程安全的快捷方式,但它根本无法很好地扩展。 考虑备受诟病的Vector类。 每个方法都是同步的,并且它的工作非常繁琐,因为迭代仍然不是线程安全的。
不是。这意味着同步方法是实现线程安全的一种方法,但它们不是唯一的方法,并且它们本身并不能保证在所有情况下都完全安全。
不必要。 例如,您可以同步(例如,锁定专用对象)访问对象变量的方法的一部分。 在其他情况下,您可以将作业委派给已经处理同步问题的某些内部对象。
有很多选择,这取决于您正在实施的算法。 虽然'synchronized'关键字通常是最简单的。
编辑
没有全面的教程,每种情况都是独一无二的。 学习它就像学习一门外语:永远不会结束:)
但肯定有用的资源。 特别是,Heinz Kabutz的网站上有一系列有趣的文章。
http://www.javaspecialists.eu/archive/Issue152.html (参见页面上的完整列表)
如果其他人有任何链接,我也有兴趣看到。 我发现整个主题非常混乱(可能是核心java中最困难的部分),特别是因为java 5中引入了新的并发机制。
玩得开心!
最一般的形式是。
不可变对象不需要同步。
此外,您可以为可变实例变量(或其中的组)使用单独的监视器/锁定,这将有助于生动。 同时只同步数据更改的部分,而不是整个方法。
synchronized methodName vs synchronized(object)
这是正确的,是另一种选择。 我认为同步访问该对象只会同步其所有方法会更有效。
虽然差异可能很微小,但如果您在单个线程中使用同一个对象,那将非常有用
即(在方法上使用synchronized关键字)
class SomeClass {
private int clickCount = 0;
public synchronized void click(){
clickCount++;
}
}
当像这样定义一个类时,一次只有一个线程可以调用click
方法。
如果在单线程应用程序中过于频繁地调用此方法会发生什么? 您将花费一些额外的时间来检查该线程是否可以在不需要时获取对象锁定。
class Main {
public static void main( String [] args ) {
SomeClass someObject = new SomeClass();
for( int i = 0 ; i < Integer.MAX_VALUE ; i++ ) {
someObject.click();
}
}
}
在这种情况下,检查线程是否可以锁定对象将被不必要地调用Integer.MAX_VALUE
(2 147 483 647)次。
因此,在这种情况下删除synchronized关键字将运行得更快。
那么,您将如何在多线程应用程序中执行此操作?
您只需同步对象:
synchronized ( someObject ) {
someObject.click();
}
向量与ArrayList
另外请注意,这种用法( syncrhonized methodName与syncrhonized(object) )顺便说一下, java.util.Vector
现在被java.util.ArrayList
替换的原因之一。 许多Vector
方法都是同步的。
大多数情况下,列表用于单线程应用程序或代码段(即jsp / servlet中的代码在单个线程中执行),Vector的额外同步对性能没有帮助。
Hashtable
被HashMap
取代也是如此
事实上,getter a也应该同步,或者字段要变得volatile
。 那是因为当你获得一些价值时,你可能会对这个价值的最新版本感兴趣。 你看, synchronized
块语义不仅提供了执行的原子性(例如,它保证了一次只有一个线程执行这个块),而且还提供了可见性。 这意味着当线程进入synchronized
块时,它会使其本地缓存无效,当它熄灭时,它会将已修改的所有变量转储回主内存。 volatile
变量具有相同的可见性语义。
不可以。即使是吸气剂也必须同步,除非他们只访问最终字段。 原因是,例如,当访问一个long值时,另一个线程当前正在写入一个微小的变化,并且只读了前4个字节而其他4个字节仍然是旧值时读取它。
对,那是正确的。 修改数据或访问可能由不同线程修改的数据的所有方法都需要在同一监视器上同步。
简单的方法是将方法标记为同步。 如果这些是长时间运行的方法,您可能只想同步读/写的那些部分。 在这种情况下,您将定义监视器,以及wait()和notify()。
简单的答案是肯定的。 如果该类的对象将由多个线程共享,则需要同步getter和setter以防止数据不一致。 如果所有线程都具有单独的对象副本,则不需要同步方法。 如果您的实例方法不仅仅是设置和获取,您必须分析等待长时间运行的getter / setter完成的线程的威胁。
您可以使用同步方法,同步块,并发工具(如Semaphore
或者如果您真的想要使用,可以使用Atomic References。 其他选项包括将成员变量声明为volatile
并使用类AtomicInteger
而不是Integer
。
这一切都取决于具体情况,但有很多可用的并发工具 - 这些只是其中的一部分。
同步可能导致hold-wait死锁,其中两个线程各自拥有一个对象的锁,并试图获取另一个线程对象的锁。
同步对于类来说也必须是全局的,并且容易犯的错误是忘记同步方法。 当一个线程持有一个对象的锁时,其他线程仍然可以访问该对象的非同步方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.