在哪些情况下,您不必同步以传播数据从一个线程到另一个线程?
尝试使用Java同步教程。它将引导您了解基础知识。同时,请注意手动通过同步控制线程是缓慢和极易出错的。其他惯用语(和语言)已经发展,可以保护开发人员免受管理原始线程的复杂性。
有一些情况下,你不必同步线程以传播数据,因为JVM会隐式地为你执行同步操作。
这些情况包括:
如果您的应用程序只有一个线程(不包括垃圾收集线程,这是虚拟机的责任),则不需要使用同步。一些库(如Swing)引入了多线程,因此您需要注意。
如果您可以坚持使用一个线程,那么就不需要进行同步。如果您需要另一个线程,则将与该线程有关的所有内容同步是避免问题的简单方法。
但是,如果您有多个线程并希望从中获得最大利益,则希望尽一切可能避免同步。在这种情况下,您唯一需要同步的时间是当您拥有一组必须与彼此一致的字段,并且其中一个或多个字段可能会被另一个线程访问时。然后,您才需要同步块。
请注意,任何非线程安全类的对象都包含整个这样的字段集,并且即使在其自己上使用时也必须进行同步:
private final Collection bigList;
synchronized (bigList) { // Protect bigList!
bigList.add( something );
}
还有一种只有一个字段的情况,例如:
private final int holderID;
if (holderID == 0) {
holderID = 2213;
// Do other stuff thinking that holderID equals 2213 and no one else will try
// to do the same other stuff, such as updating databases or writing to other
// fields.
}
这确实需要同步。否则,如果您的CPU至少有10,000个核心,那么10,000个线程都可以同时执行此行(),看到holderID中的零,将该值设置为自己的数字,并造成严重后果。但是,自从1.5以来,您可以使用java.util.concurrent.atomic包来避免传统的、缓慢的同步。
在最小化同步多线程方面,您必须非常小心。仔细阅读,深思熟虑,并设计测试,使所有线程保持运行数分钟。使每个类和实例字段都不是最终易失性的。永远不要相信一个类或实例字段。尽快将所有值放入本地变量中,并尽可能长时间地保留它们。例如,不要这样做:
if (mainList != null) mainList.get( 1 );
在两个引用之间,某些线程绑定到空的mainList。你可以同步它,但最好做:
MainList ml = mainList;
if (ml != null) ml = ml.get( 1 );