Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java线程安全适用于类的所有实例还是仅适用于共享实例?_Java_Multithreading_Concurrency_Thread Safety_Static Methods - Fatal编程技术网

Java线程安全适用于类的所有实例还是仅适用于共享实例?

Java线程安全适用于类的所有实例还是仅适用于共享实例?,java,multithreading,concurrency,thread-safety,static-methods,Java,Multithreading,Concurrency,Thread Safety,Static Methods,我试图确定我是否需要在我编写的几个关键类中担心线程安全。我已经阅读了几篇文章/现有的SO问题,我一直看到相同的、反复出现的线程安全定义: 线程安全意味着一个对象或类的字段始终保持有效状态,正如其他对象和类所观察到的那样,,即使多个线程同时使用。 嗯。我有点明白了。但我在这里遗漏了一个很大的谜题: 只有当一个类的同一个实例被多个线程使用时,或者只有当任何2个以上的实例被使用时,线程安全才会起作用吗? 示例#1: 假设我有一个Dog类,它不包含静态方法或字段,假设我有5个不同的Dog实例,它们在5

我试图确定我是否需要在我编写的几个关键类中担心线程安全。我已经阅读了几篇文章/现有的SO问题,我一直看到相同的、反复出现的线程安全定义:

线程安全意味着一个对象或类的字段始终保持有效状态,正如其他对象和类所观察到的那样,,即使多个线程同时使用。

嗯。我有点明白了。但我在这里遗漏了一个很大的谜题:

只有当一个类的同一个实例被多个线程使用时,或者只有当任何2个以上的实例被使用时,线程安全才会起作用吗?


示例#1: 假设我有一个
Dog
类,它不包含静态方法或字段,假设我有5个不同的
Dog
实例,它们在5个不同的线程中运行。我需要关注线程安全吗?我会说“不”,因为没有静态字段/方法,每个线程都有自己的
Dog
实例,其状态独立于其他4个实例(1)这是正确的吗?如果没有,原因是什么?


示例2: 现在让我们假设我向
Dog
添加了一个静态方法:

public void woof() {
    this.isWoofing = true;
}

public static void makeWoof(Dog dog) {
    dog.woof();
}
我现在需要关注线程安全吗?每个线程都有自己的
狗的实例
,但现在它们共享相同的静态
makeWoof()
方法,该方法更改它所操作的
狗的状态。我还是说“不”(2)这是正确的吗?如果没有,原因是什么?


给出这两个例子,在我看来,当多个线程在同一个类实例上运行时,线程安全只是一个问题。但是当你给每个线程一个自己的实例时,我似乎可以对这个实例做任何我想做的事情,而不用担心其他线程内部发生了什么(3)这是正确的吗?若否,原因为何?他们有例外吗?提前谢谢

您关于线程安全性的假设是正确的。它归结为同一个
实例上的字段/状态被多个线程修改

在您的第一个问题中,所有线程都操作自己的
Dog
实例,因此该方法是线程安全的

在第二个问题中,实例被传递到静态方法中,因此您可以再次使用该类的不同实例。如果
Dog
类包含静态字段,并且静态方法操作了那些不会是线程安全的字段,但是非最终静态字段永远不会是线程安全的。

这与共享实例无关。这是关于共享状态的

考虑到这一点:

1:正确-如果您的线程不在共享状态下运行,那么它们本质上是线程安全的

2:不正确(sorta):-在这个特定静态方法的特定示例中,没有触及共享状态,但是静态方法可以创建/管理共享状态

3:见1和2

例如,让我们在OP的
Dog
类中引入一点共享状态:

    static Integer numberOfBarksToday=0; 
因为它是静态的,所以是共享的。所以现在,静态方法(OP的
makeWoof
方法的修改版本)可以处理:

    public static void makeWoof(Dog dog) {
        dog.woof();
        synchronized(Dog.numberOfBarksToday) {
            Dog.numberOfBarksToday++;
        }
    }       
我刚刚意识到,当我创建上述示例时,出于习惯,我同步了访问。通过这种同步,这种特定的访问是线程安全的(当然,对numberOfBarksToday的所有其他访问也必须同步)

如果没有同步,如果多个线程调用此方法,您将倾向于低估当前的巴克数: T0)Barkstoday的编号=0; T1)线程A检查树皮的数量(第一部分
++
),得到0。 T2)线程B检查树皮的数量,得到0。 T3)将一组树皮穿至1 T4)螺纹B将树皮数设置为1

这并不考虑在共享对象中,赋值方法是否是原子的

同步阻止了上述所有操作,并引入了内存屏障,以便所有线程都能看到numberOfBarksToday的相同值。

(1)是

(2) 是,如果该方法不在任何非最终静态字段上运行。不,否则

(3) 是的,有关例外情况,请参见(2)。

示例#1-您是正确的

如果我们可以证明某个对象对于单个线程来说是唯一可见的,那么它就被称为线程受限对象。线程受限对象不存在线程安全问题。。。而您的
实例是线程限制的

例子2——你是对的

您使用的是
static
方法,这一事实在这里没有任何改变。给定您描述的示例,
Dog
实例仍然是线程限制的

示例#3-假设所考虑的所有对象都是线程限制的,没有例外


请注意,如果将示例2中的
woof
方法更改为使用某些共享状态(例如
静态
变量),则线程安全可能会成为一个问题

另一件需要注意的事情是,可能很难知道实例是否以及何时将被线程限制。有两种应对策略:

  • 通过使相关方法线程安全,您可以使
    Dog
    类线程安全。这意味着您不需要分析使用模式,但您的应用程序最终可能会执行不必要的同步

  • 如果需要,您可以将其交给使用
    Dog
    类进行外部同步的类


您询问了静态方法如何可能引入线程问题。下面我提供了一个带有注释的代码示例。请注意,没有任何东西可以阻止非静态方法
public class Dog
{
    private static boolean isWagging;
    private boolean isWoofing;

    public void woof()
    {
        this.isWoofing = true;
    }

    public static void wag()
    {
        isWagging = true;
    }

    public static void makeWoof(Dog dog)
    {
        /**
         * Thread safety: woof() only modifies instance variables of 'Dog'.
         * So if no dog instances are shared between threads, then no
         * instance variables of any Dog are shared between threads, and so
         * (as long as we do not share any Dog across threads) then there is
         * no concern about needing to control access to shared state. Note
         * that woof() *could* be changed to also modify 'isWagging' which
         * is a static variable, and thus not protected by the "Dog
         * instances are not shared between threads" contract. There is no
         * guarantee that just because a method is an "instance" method, it
         * will not modify shared state. It is a good general practice for
         * member methods to only modify member variables, but sometimes
         * modifying shared state (e.g. a database) in a member method is
         * somewhat unavoidable.
         */
        dog.woof();

        /**
         * Thread safety: wag() is a static method that operates on a static
         * variable. Instances of Dog do not get separate copies of static
         * variables, as the nature of 'static' means that the variable is
         * attached to the Dog _class_ itself, not to _instances_ of the Dog
         * class. You could say that, in the current implementation, if
         * *any* dog wags, then all dogs will be marked as wagging, which is
         * probably not what we want. Additionally, since there is no
         * synchronization mechanism being used, there is no guarantee that
         * other threads will see that that the value of 'isWagging' has
         * been updated.
         */
        wag();

        /**
         * Additional note: Java makes the static/non-static issue confusing
         * by allowing the following syntax to compile. The following syntax
         * *might* lead some programmers to believe that some dogs can be
         * wagging while others are not. Most compilers will warn you about
         * this syntax because it misleadingly makes it appear as if
         * isWagging is an instance variable, and wag is an instance method
         * (which is not the case.)
         */
        if (!dog.isWagging)
        {
            dog.wag();
        }

        /**
         * To be less ambiguous, you should really write the above code as:
         */
        if (!isWagging)
        {
            wag();
        }

        /**
         * Or even better: do not use any non-final static variables in your
         * program at all.
         */
    }
}