尝试掌握java中的基本线程同步

尝试掌握java中的基本线程同步,java,multithreading,synchronized,Java,Multithreading,Synchronized,我用各种同步技术尝试了这段代码,我想确保我了解发生了什么。我已经读了很多关于这方面的文章,但是没有一篇对我来说足够详细 下面是我观察到的: 同步:这只适用于我给所有线程提供相同的Threadtest实例的情况,因为如果我给每个线程提供自己的实例,每个线程都将获得该实例的内在锁,并且可以在不中断其他线程的情况下访问方法 然而,如果我给每个线程提供它自己的实例,我可以做:Synchronized getClass,因为这样我就得到了类的本质锁 或者,我可以这样做:Synchronized mydat

我用各种同步技术尝试了这段代码,我想确保我了解发生了什么。我已经读了很多关于这方面的文章,但是没有一篇对我来说足够详细

下面是我观察到的:

同步:这只适用于我给所有线程提供相同的Threadtest实例的情况,因为如果我给每个线程提供自己的实例,每个线程都将获得该实例的内在锁,并且可以在不中断其他线程的情况下访问方法

然而,如果我给每个线程提供它自己的实例,我可以做:Synchronized getClass,因为这样我就得到了类的本质锁

或者,我可以这样做:Synchronized mydate,这里的规则与Synchronized mydate相同。但它有不公开的优势。>我真的不明白。使用这个有什么危险

除了同步的getClass之外,我还可以使用一个私有静态字段。 但是,我不能做同步Date.class

我可以同步整个方法,效果与同步块相同

使计数器易失性不起作用,因为递增不是真正的原子操作

如果我想单独访问每个方法,我会创建三个私有字段,并在同步块中使用它们。然后,我有效地使用了这些字段的内部锁,而不是我的类或实例的内部锁

我还注意到,当我使用类锁时,每个方法都被视为独立的,并且我有3个计数器,它们都指向15。如果使用实例锁,计数器将转到45。这是正确的和预期的行为吗

我的解释和观察正确吗?我基本上是想确保从控制台输出中得出正确的结论,我得到了正确的答案 b正确 c在应用程序的另一部分中,如果可以访问您的类,则可能会有其他一些代码使用您的this或类。这意味着不相关的代码将等待彼此完成。 d由于上述相同原因,您无法在Date.class上进行同步。可能会有不相关的线程方法不必要地相互等待。 方法同步与类锁相同 g正确

a正确 b正确 c在应用程序的另一部分中,如果可以访问您的类,则可能会有其他一些代码使用您的this或类。这意味着不相关的代码将等待彼此完成。 d由于上述相同原因,您无法在Date.class上进行同步。可能会有不相关的线程方法不必要地相互等待。 方法同步与类锁相同 g正确

a-c;e-f是正确的

c或者,我可以这样做:Synchronized mydate,适用于Synchronized的相同规则。但它有不公开的优势。>我真的不明白。使用这个有什么危险

理由是其他代码也可能决定将该对象用作锁。这可能导致冲突;当你知道这种情况永远不会发生时,那就不是什么坏事了。当在代码中使用wait/notify时,这通常也是一个更大的问题

d除了同步的getClass之外,我还可以使用一个私有静态字段。但是,我不能做同步Date.class

您可以使用Date.class,这会有点奇怪,并且会陷入上面在c中讨论的关于不污染其他类工作空间的争论中

g如果我想使每个方法都可以单独访问,我会创建三个私有字段,并在同步块中使用它们。然后,我有效地使用了这些字段的内部锁,而不是我的类或实例的内部锁

假设这三个方法共享相同的状态,那么不,这将是不明智的,因为这将导致线程之间的竞争

h我还注意到,当我使用类锁时,每个方法都被视为单独的,我有3个计数器,有效地指向15。如果使用实例锁,计数器将转到45。这是正确的和预期的行为吗

不,这听起来不对,但我可能误解了你。当使用this或this.getClass作为锁时,我希望这两种情况下的总数都是45;e-f是正确的

c或者,我可以这样做:Synchronized mydate,适用于Synchronized的相同规则。但它有不公开的优势。>我真的不明白。使用这个有什么危险

理由是其他代码也可能决定将该对象用作锁。这可能导致冲突;当你知道这种情况永远不会发生时,那就不是什么坏事了。当在代码中使用wait/notify时,这通常也是一个更大的问题

d除了同步的getClass之外,我还可以使用一个私有静态字段。然而,我不能 不要上课

您可以使用Date.class,这会有点奇怪,并且会陷入上面在c中讨论的关于不污染其他类工作空间的争论中

g如果我想使每个方法都可以单独访问,我会创建三个私有字段,并在同步块中使用它们。然后,我有效地使用了这些字段的内部锁,而不是我的类或实例的内部锁

假设这三个方法共享相同的状态,那么不,这将是不明智的,因为这将导致线程之间的竞争

h我还注意到,当我使用类锁时,每个方法都被视为单独的,我有3个计数器,有效地指向15。如果使用实例锁,计数器将转到45。这是正确的和预期的行为吗


不,这听起来不对,但我可能误解了你。当使用this或this.getClass作为锁时,我预计在这两种情况下,总数都是45。

如果您在持有锁的同时向控制台写入代码时速度慢,那么您的代码是线程安全的,但正确且慢比错误且快要好

一个同步的方法是:只有当我给所有线程提供相同的Threadtest实例时,这才有效,因为如果我给每个线程提供自己的实例,每个线程都将获得该实例的内在锁,并且可以在不中断其他线程的情况下访问这些方法

无论哪种情况,您的代码都是线程安全的——也就是说,它每次都会给出完全相同的结果。如果将同一个实例传递给三个不同的线程,那么输出的最后一行将是345,因为只有一个计数器变量,如果为每个线程提供自己的实例,那么将有三行读取315。我觉得你似乎明白这一点

但是,如果我给每个线程提供它自己的实例,我可以这样做:Synchronized getClass,因为这样我就得到了类的本质锁

如果您这样做,您的代码仍然是线程安全的,但是您将得到三行代码,阅读3 15,如上所述。请注意,由于以下原因,您也更容易出现活跃性和死锁问题

c或者,我可以这样做:Synchronized mydate,适用于Synchronized的相同规则。但它的优点是不公开。我真的不明白。使用这个有什么危险

你应该尽可能使用私人锁。如果您使用全局可见的对象,例如this或getClass,或者可见性不是private或interned字符串的字段,或者您从工厂获得的对象,那么您就有可能会有其他代码试图锁定您正在锁定的对象。您可能会等待更长的时间,甚至在死锁的情况下

有关可能出现问题的详细分析,请参阅-但请注意,这不仅仅是一个安全问题

d除了同步的getClass之外,我还可以使用一个私有静态字段。但是,我不能做同步Date.class

出于上述原因,私有静态字段比getClass或Date.class更可取

我可以同步整个方法,效果与同步块相同

目前几乎有一些,但同样,您应该更喜欢私有锁

使计数器易失性不起作用,因为递增不是真正的原子操作

是的,您可能会遇到竞争条件,并且您的代码不再是线程安全的,尽管您没有下面提到的可见性问题

g如果我想使每个方法都可以单独访问,我会创建三个私有字段,并在同步块中使用它们。然后,我有效地使用了这些字段的内部锁,而不是我的类或实例的内部锁

您不应该这样做,您应该始终使用相同的锁来访问变量。除了可以让多个线程同时读/写同一个变量这一事实之外,在给定竞争条件的情况下,线程间可见性也存在一个更微妙的问题。在释放锁之前由一个线程完成的Java内存模型在另一个线程获得相同的锁时将被另一个线程看到。因此,执行upCounter2的线程2可能会看到线程1执行upCounter1的结果,也可能不会看到

而不是思考我需要执行哪些代码块?你应该想想我需要访问哪个州

h我还注意到,当我使用类锁时,每个方法都被视为独立的,我有3个计数器,可以有效地计算到15。如果使用实例锁,计数器将转到45。这是正确的和预期的行为吗

是的,但它与用于同步的对象无关,而是因为您创建了三个不同的ThreadTest对象,因此有三个不同的计数器,正如我在回答第一个问题时所解释的

确保
您了解在一个对象上运行的三个线程与在三个不同对象上运行的一个线程之间的区别。然后,您将能够理解三个线程在三个不同对象上运行时所观察到的行为。

您的代码是线程安全的,如果您在持有锁的情况下向控制台写入代码时速度较慢,但正确且缓慢要比错误且快速好

一个同步的方法是:只有当我给所有线程提供相同的Threadtest实例时,这才有效,因为如果我给每个线程提供自己的实例,每个线程都将获得该实例的内在锁,并且可以在不中断其他线程的情况下访问这些方法

无论哪种情况,您的代码都是线程安全的——也就是说,它每次都会给出完全相同的结果。如果将同一个实例传递给三个不同的线程,那么输出的最后一行将是345,因为只有一个计数器变量,如果为每个线程提供自己的实例,那么将有三行读取315。我觉得你似乎明白这一点

但是,如果我给每个线程提供它自己的实例,我可以这样做:Synchronized getClass,因为这样我就得到了类的本质锁

如果您这样做,您的代码仍然是线程安全的,但是您将得到三行代码,阅读3 15,如上所述。请注意,由于以下原因,您也更容易出现活跃性和死锁问题

c或者,我可以这样做:Synchronized mydate,适用于Synchronized的相同规则。但它的优点是不公开。我真的不明白。使用这个有什么危险

你应该尽可能使用私人锁。如果您使用全局可见的对象,例如this或getClass,或者可见性不是private或interned字符串的字段,或者您从工厂获得的对象,那么您就有可能会有其他代码试图锁定您正在锁定的对象。您可能会等待更长的时间,甚至在死锁的情况下

有关可能出现问题的详细分析,请参阅-但请注意,这不仅仅是一个安全问题

d除了同步的getClass之外,我还可以使用一个私有静态字段。但是,我不能做同步Date.class

出于上述原因,私有静态字段比getClass或Date.class更可取

我可以同步整个方法,效果与同步块相同

目前几乎有一些,但同样,您应该更喜欢私有锁

使计数器易失性不起作用,因为递增不是真正的原子操作

是的,您可能会遇到竞争条件,并且您的代码不再是线程安全的,尽管您没有下面提到的可见性问题

g如果我想使每个方法都可以单独访问,我会创建三个私有字段,并在同步块中使用它们。然后,我有效地使用了这些字段的内部锁,而不是我的类或实例的内部锁

您不应该这样做,您应该始终使用相同的锁来访问变量。除了可以让多个线程同时读/写同一个变量这一事实之外,在给定竞争条件的情况下,线程间可见性也存在一个更微妙的问题。在释放锁之前由一个线程完成的Java内存模型在另一个线程获得相同的锁时将被另一个线程看到。因此,执行upCounter2的线程2可能会看到线程1执行upCounter1的结果,也可能不会看到

而不是思考我需要执行哪些代码块?你应该想想我需要访问哪个州

h我还注意到,当我使用类锁时,每个方法都被视为独立的,我有3个计数器,可以有效地计算到15。如果使用实例锁,计数器将转到45。这是正确的和预期的行为吗

是的,但它与用于同步的对象无关,而是因为您创建了三个不同的ThreadTest对象,因此有三个不同的计数器,正如我在回答第一个问题时所解释的


确保您了解在一个对象上运行的三个线程与在三个不同对象上运行的一个线程之间的区别。然后,您将能够理解三个线程在三个不同对象上运行时所观察到的行为。

您还没有定义什么是“正确”行为。您是否只想保证计数器达到其预期值?它的预期最终值是多少?您想避免交错打印语句吗?很抱歉,回复太晚:因此,我真的不希望计数器的任何行为execpt不会交错或跳跃,这在没有同步的情况下会发生。我只是看了一下控制台的输出,然后想啊,那个输出就是pr
很可能是这样,因为……你还没有定义什么是“正确的”行为。您是否只想保证计数器达到其预期值?它的预期最终值是多少?您想避免交错打印语句吗?很抱歉,回复太晚:因此,我真的不希望计数器的任何行为execpt不会交错或跳跃,这在没有同步的情况下会发生。我只是看了一下控制台的输出,然后想啊,这个输出可能在那里,因为…你的回答应该是+5左右,关于污染其他类工作空间的评论,但出于某种原因,+1是我最多可以给出的。你的回答应该是+5左右,关于污染其他类工作空间的评论,但出于某种原因,+1是我最多可以给出的。
public class ThreadTest implements Runnable {

private int counter;
private Date mydate = new Date();

public void upCounter1() {
    synchronized (mydate ) {
        for (int i = 0; i < 5; i++) {
            counter++;
            System.out.println("1 " + counter);
        }
    }

}

public void upCounter2() {
    synchronized (mydate ) {
        for (int i = 0; i < 5; i++) {
            counter++;
            System.out.println("2 " + counter);
        }
    }
}

public void upCounter3() {
    synchronized (mydate ) {
        for (int i = 0; i < 5; i++) {
            counter++;
            System.out.println("3 " + counter);
        }
    }
}

@Override
public void run() {
    upCounter1();
    upCounter2();
    upCounter3();
}

public static void main(String[] args) {
    Threadtest mtt = new Threadtest();
    Thread t1 = new Thread(mtt);

    Thread t2 = new Thread(mtt);

    Thread t3 = new Thread(mtt);

    t1.start();
    t2.start();
    t3.start();
}

}