【问题标题】:Thread-Safety and Instances in javajava中的线程安全和实例
【发布时间】:2017-10-24 10:44:34
【问题描述】:

我已经阅读了很多关于线程安全的内容,并且对于是否有多个对象实例会影响线程安全感到困惑。这是一个详细说明的示例:
假设我们有一个名为 RGBColor 的类,用户将设置红色、绿色和蓝色的值,然后返回颜色。

Public Class RGBColor {
    private int red;
    private int green;
    private int blue;
    Public RGBColor(int red, int green, int blue){
       this.red = red;
       this.green = green;
       this.blue = blue;
    }
    Public void setColor(int red, int green, int blue){
       this.red = red;
       this.green = green;
       this.blue = blue;
    }
    Public RGBColor getColor(){
       return this;
    }
}

现在如果程序创建了该类的多个实例,例如:

RGBColor red = new RGBcolor(255,0,0);
RGBColor blue = new RGBcolor(0,0,255);

现在问题来了。类的这些实例是完全独立的吗?我的意思是线程安全会是一个问题吗?毕竟据我了解,它们应该是完全不同的对象,在 RAM 中具有不同的分配。

另一个问题是变量和方法是否是静态的,例如。

Public Class RGBColor {
    private static int RED;
    private static int GREEN;
    private static int BLUE;

    Public static void setColor(int red, int green, int blue){
       RED = red;
       GREEN = green;
       BLUE = blue;
    }
}

在线程安全方面如何处理静态变量和方法?

ps:我已经更新了第二个示例,因为它有缺陷。

【问题讨论】:

  • 第一个问题的答案是肯定的。对象中的方法是“独立的”。对对象调用方法仅影响该对象。问题 2 的答案是您必须同步呼叫。在您的示例中,所有线程都会更改相同的值,所以也许没关系。顺便说一句,你不能在静态方法中调用this
  • @JackFlamp 好的,非常感谢您的澄清,我也更新了帖子。我完全错过了我一直在对静态变量调用this。非常感谢:)

标签: java multithreading static thread-safety multiple-instances


【解决方案1】:

在对象具有实例变量的第一种情况下,对象彼此独立存在。修改一个不会影响其他。

在第二种情况下,静态字段属于类,而不属于任何一个对象。构造函数用于创建实例?不是为了填充静态变量,在这种情况下有一个构造函数是令人困惑和不必要的。在 getColor 中使用它也不起作用,静态方法中没有 RGBColor 的实例。

对于第一种情况,如果您去掉 setter 并将实例变量设置为 final,那么您的对象将是不可变的,从而使它们可以安全地在多个线程中使用。

对于第二种情况,多个线程可以覆盖彼此的工作,并且没有任何东西可以让其他线程看到更改。 (仅仅因为一个线程改变了一个值并不意味着它立即对其他线程可见。)

如果你真的想在这个类范围内有一组值,你可以创建一个不可变对象并将其分配给一个静态 volatile 变量,这样更新将是原子的并且对其他线程可见。

【讨论】:

    【解决方案2】:

    您不应该在这里担心“线程安全”。因为你得到了更基本的东西错误。当对应的字段都是静态的时,拥有一个类的多个实例是没有意义的。

    换句话说:在您的第二个示例中,拥有redblue RGBColor 对象是没有意义的——因为您实例化的所有对象都将相互覆盖!最后一个获胜...

    除此之外,在您的第一个示例中:setColor() 方法可能存在线程安全问题。因为该方法可能使您最终处于不一致的状态(当两个线程在“相同”时间调用 same 对象上的设置器时 - 但使用不同的参数)。

    换句话说:

    • 首先花一些时间了解 static 是什么,以及为什么您的第二个代码输入如此“糟糕”
    • 那么请理解:只要多个线程可以访问“共享”数据,线程安全就很重要。因为那时您必须确保对“共享”数据的读/写操作始终定义良好。

    【讨论】:

    • 我知道第二个示例有缺陷,我只是对调用 static 方法时的部分感到困惑,我是否需要将 setColor 设置为 synchronized 方法,正如我所理解的那样这里的答案是必需的。谢谢你的回答。
    【解决方案3】:

    类的这些实例是完全独立的吗?我的意思是 线程安全会是个问题吗?

    实例在它们之间是独立的,是的。
    线程安全问题是多个线程可能以并发方式访问一个实例,并且其中一个或两个对实例状态进行一些修改。

    例如,假设您创建了一个 RGBColor 的实例,并且多个线程可以操作该实例。
    现在,我们要求您要求 setColor() 调用不应与自身的其他调用交错。
    在那里,你有一个竞争条件,你应该处理它。
    要处理它,您可以使用 synchronized 语句包围 setColor() 语句并设置字段 volatile 以确保始终更新每个线程的内存:

    private volatile int red;
    private volatile int green;
    private volatile int blue;
    ...
    public void setColor(int red, int green, int blue){
       synchronized (this){
         this.red = red;
         this.green = green;
         this.blue = blue;
       }
    }
    

    静态变量和方法是如何处理的 谈到线程安全?

    对于静态变量,方法与实例变量相同。

    对于实例方法,可以在实例上的锁上实现线程安全。
    对于静态方法,应该实现对类本身的加锁。

    【讨论】:

    • 谢谢你的回答,但实际上你在这里提出了另一个问题,我怎么知道两个线程是否访问了同一个对象的实例?
    • @Hosam Gemei 不客气。如果您不知道是否有多个线程访问同一个对象,我认为 Oracle 线程一章是一个很好的入门:docs.oracle.com/javase/tutorial/essential/concurrency/…
    【解决方案4】:

    回答您的第一个问题。 YES 不同的对象在 JVM 中会有不同的哈希码。但是,当您考虑同时访问相同对象的事务时,您将不得不对对象使用线程安全(如果有对象修改),您可以使用

    同步

    methods 中的修饰符,以确保线程安全。当且仅当同一个对象同时被不同的系统访问。

    回答你的第二个问题。 java 中的静态或非静态对象只有在对象的修饰符将同步时才是线程安全的。除此之外,您还可以使用线程池和倒计时锁来有效地处理具有多个线程的对象

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-13
      • 1970-01-01
      相关资源
      最近更新 更多