如何避免在Java中创建线程敌对类
在有效的java第二版第70项中,Josh Bloch解释了线程类 即使所有方法 调用被外部同步所包围。线 敌意通常是由于修改静态数据而没有 同步如何避免在Java中创建线程敌对类,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,在有效的java第二版第70项中,Josh Bloch解释了线程类 即使所有方法 调用被外部同步所包围。线 敌意通常是由于修改静态数据而没有 同步 有人能举例说明,如果类在没有内部同步的情况下修改共享静态数据,那么如何通过外部同步实现线程安全是不可能的吗?可能是一个人为的例子,但该类不可能在外部同步,因为该值可以从类外部访问: public class Example { public static int value; public void setValue(int newValu
有人能举例说明,如果类在没有内部同步的情况下修改共享静态数据,那么如何通过外部同步实现线程安全是不可能的吗?可能是一个人为的例子,但该类不可能在外部同步,因为该值可以从类外部访问:
public class Example {
public static int value;
public void setValue(int newValue) {
this.value = newValue;
}
}
无论您如何同步setter的调用,都不能保证其他线程不会更改
值引用假定对同一对象实例上的类方法的每次调用都是同步的。例如,考虑下面的类:
public class Test {
private Set<String> set = new TreeSet<>();
public void add(String s) {
set.add(s);
}
}
如果使用相同的t
从多个线程调用safeAdd
,则它们将相互排斥。如果使用不同的t
,也可以更新独立对象
但是,我们认为<代码> SET>代码>为静态:
private static Set<String> set = new TreeSet<>();
private static Set=new TreeSet();
这样,即使是不同的Test
对象也可以访问共享集合。因此,在这种情况下,Test
实例上的同步将没有帮助,因为相同的集合
可能仍然会从不同的Test
实例中同时修改,这可能会导致数据丢失、随机异常、无限循环或其他情况。因此,这样的阶级是敌对的
有人能举例说明,如果类在没有内部同步的情况下修改共享静态数据,那么通过外部同步实现线程安全是不可能的吗
这不是不可能的。如果类具有访问全局(即,静态
)数据的方法,则可以通过在全局锁上同步来实现线程安全
但是强制调用方在一个全局锁上同步线程仍然是线程敌对的。大型全局锁可能是多线程应用程序中的一个严重瓶颈。作者希望你做的是设计你的类,这样客户端就可以为类的每个实例使用一个单独的锁。那么,如果我们在内部同步setter方法……它会使类线程安全吗?当你说“在内部同步”时,你在想什么确切的变化?“不可能进行外部同步,因为该值是公共的”,这听起来很奇怪。应该是“外部同步不会使代码线程安全”“。原因与值是公共的无关。如果我只是在setter方法签名中添加synchronized,或者如果我使用私有锁对象并使用该锁对象同步setter方法,这就是我所说的同步internally@ArghyaSadhu这并不能保证线程安全,因为任何东西都可以访问该值,而不考虑锁:它在类外部可见。因此,现在使该类线程安全的唯一可能方法是在内部对其进行同步,即将add方法设置为静态同步,从而获得类对象本身的锁,对吗?@ArghyaSadhu,您仍然可以在类对象本身上进行外部同步,但这很难看,因为该方法是非静态的,因此不需要以不安全的方式修改全局状态。更好的选择是使用并发友好的数据结构,如ConcurrentSkipListSet
@TagirValeev…是的,这是可能的,因为我们有一个并发友好的数据结构。我想知道是否有任何情况下,即使与类对象进行外部同步也不会使该类线程safe@ArghyaSadhu我想你对Josh想要传达的东西有点困惑。这一部分简单地描述了“线程安全级别”,而不是“如果您这样做,那么您将成为特定级别”。e、 它不是“如果您访问静态变量,那么您将变得不友好”。总有办法使它线程安全。它的意思是:“线程恶意代码意味着即使您在实例上进行外部同步,它也不是线程安全的,这通常是由静态var访问引起的”希望您看到difference@ArghyaSadhu,您始终可以创建单个全局锁并同步其中的所有内容,从而使应用程序完全安全(尽管它基本上会变成单线程)。
private static Set<String> set = new TreeSet<>();