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

时间:2014-10-22 16:35:27

标签: java multithreading synchronized

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

}

我尝试了各种同步技术的代码,并且我想确保我得到了正在发生的事情。我已经阅读了很多关于这方面的文章,但是没有一篇文章能够为我解读。

所以这就是我观察到的:

  1. synchronised (this):如果我将Threadtest的SAME实例提供给所有线程,这只是有效的,因为如果我给每个线程自己的实例,每个都将获得该实例的内部锁定和可以在不中断其他线程的情况下访问这些方法。

  2. 但是,如果我给每个线程都有自己的实例,我可以这样做:synchronised (getClass()),因为那时我得到了类的内在锁

  3. 或者,我可以执行:synchronised (mydate),其中适用于synchronized(this)的相同规则适用。但它的优点是不公开。 &GT;我真的不明白这一点。什么是&#34;危险&#34;使用this

  4. synchronised (getClass())外,我还可以使用私有静态字段。 但是,我不能synchronised(Date.class)

  5. 我可以同步整个方法(与synchronized-block一样有效)

  6. 使计数器变量不起作用,因为递增不是真正的原子操作

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

  8. 我还注意到,当我使用类锁时,每个方法都被视为独立的,我实际上有3个转到15个。如果我使用实例锁,则计数器转到45.是正确和预期的行为?

  9. 我的解释和观察是否正确? (我基本上想确保从我得到的控制台输出中得出正确的结论)

3 个答案:

答案 0 :(得分:3)

A-C; e-f是正确的。

  

c)或者,我可以这样做:synchronized(mydate),适用于synchronized(this)的相同规则适用。但它的优点是不公开。 &GT;我真的不明白这一点。什么是&#34;危险&#34;使用这个?

参数是其他代码也可能决定将该对象用作锁。这可能会导致冲突;当你知道这种情况永远不会如此时,那就不是一件坏事。当在代码中使用wait / notify时,通常也会出现问题。

  

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

你可以使用Date.class,它只是有点奇怪,并且属于上面讨论的关于不污染其他类工作空间的论点。

  

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

鉴于这三种方法共享相同的状态,那么不,这不会是明智的,因为它会导致线程之间的竞争。

  

h)我还注意到,当我使用类锁时,每个方法都被视为独立的,我实际上有3个计数器转到15.如果我使用实例锁,则计数器转到45.是正确和预期的行为?

不,这听起来不对,但我可能误解了你。在使用this或this.getClass()作为锁时,我希望在两种情况下总数为45。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

你的代码是线程安全的,如果它很慢(你在拿着锁的同时写入控制台) - 但更好的纠正和缓慢而不是错误和快速!

  

a)synchronised (this):如果我将Threadtest的SAME实例提供给所有线程,这只是有效的,因为如果我给每个线程自己的实例,每个都将获得该实例的内部锁定并且可以从其他线程中不间断地访问方法。

你的代码也是线程安全的 - 也就是说,它每次都会得到完全相同的结果。如果将同一个实例传递给三个不同的线程,则输出的最后一行将是&#34; 3 45&#34; (因为只有一个计数器变量),如果你给每个线程都有自己的实例,那么将有三行读取&#34; 3 15&#34;。听起来像你理解这一点。

  

b)但是,如果我给每个线程自己的实例,我可以做:synchronised (getClass()),因为那时我得到了类的内在锁

如果你这样做,你的代码仍然是线程安全的,但你会得到三行读取&#34; 3 15&#34;如上。请注意,由于下述原因,您也更容易出现活动和死锁问题。

  

c)或者,我可以执行:synchronised (mydate),其中适用于synchronised (this)的相同规则适用。但它的优点是不公开。我真的不明白这一点。什么是&#34;危险&#34;使用this

你应该尝试使用私人锁。如果您使用全局可见对象(例如thisgetClass或具有private以外的可见性的字段或实习String或您从工厂获得的对象)然后你打开了一些其他代码也会尝试锁定你锁定的对象的可能性。您可能最终等待的时间超过预期获得锁定(活跃度问题)或甚至陷入死锁状态。

有关可能出错的问题的详细分析,请参阅secure coding guidelines for Java - 但请注意,这不仅仅是一个安全问题。

  

d)除了synchronised (getClass())之外,我还可以使用私有静态字段。但是,我不能synchronised(Date.class)

由于上述原因,private static字段优先于getClass()Date.class

  

e)我可以同步整个方法(与synchronized-block相同的效果)

相当多(目前有一些insignificant byte code differences),但你应该更喜欢私人锁。

  

f)使计数器volatile不起作用,因为递增不是一个真正的原子操作

是的,您可能会遇到竞争状况而且您的代码不再是线程安全的(尽管您没有下面提到的可见性问题)

  

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

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

而不是考虑&#34;我需要执行哪些代码块?&#34;你应该思考&#34;我需要访问哪些州?&#34;。

  

h)我还注意到,当我使用类锁时,每个方法都被视为独立的,并且我有效地使用了3个15位。如果我使用实例锁,则计数器转到45.是正确和预期的行为?

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

确保您了解在一个对象上操作的三个线程和在三个不同对象上操作的一个线程之间的区别。然后,您将能够通过在三个不同对象上运行的三个线程来了解您正在观察的行为。