如何避免在Java中创建线程敌对类

如何避免在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

在有效的java第二版第70项中,Josh Bloch解释了线程类

即使所有方法 调用被外部同步所包围。线 敌意通常是由于修改静态数据而没有 同步


有人能举例说明,如果类在没有内部同步的情况下修改共享静态数据,那么如何通过外部同步实现线程安全是不可能的吗?

可能是一个人为的例子,但该类不可能在外部同步,因为该值可以从类外部访问:

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<>();