简体   繁体   English

同步应该在成员类中还是在包装器类中发生?

[英]Should synchronization happen in the member class or in the wrapper class?

First, what I've read through: 首先,我已经阅读了以下内容:

And I followed the myriad links to duplicates listed on most of those posts. 而且,我跟踪了无数链接,这些链接都指向大多数这些帖子中列出的重复项。 So I apologize ahead of time if this is a duplicate. 因此,如果这是重复的话,我提前致歉。 I don't feel my question was answered by any of those, or the subsequent links. 我觉得这些问题或后续链接都没有回答我的问题。 But then again, I am asking now because I have no clue as to what is going on here. 但是话又说回来,我现在问是因为我对这里发生的事情一无所知。 Now on to the main event... 现在开始主要活动...

I have a pair of classes, A and B . 我有一对班, AB Class B has an instance of A as a member: B类有一个A实例作为成员:

Class A: A类:

public class A {
    private final int count;

    public A(int val) {
        count = val;
    }

    public int count() { return count; }

    public A incrementCount() {
        return new A(count + 1);
    }

    public void doStuff(long i) {
        try {
            Thread.sleep(i * 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Class B: B级:

public class B implements Runnable{
    private A a;

    public B() { a = new A(0); }

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
                a.doStuff(i);
                a = a.incrementCount();
            }
    }
}

And I have a class that takes an instance of B and passes it to two threads, starts both threads, and then lets them do their thing: 我有一个类,它接受B的实例并将其传递给两个线程,启动两个线程,然后让他们做自己的事情:

public class App {
    public static void main(String[] args) {
        B b = new B();
        Thread b1 = new Thread(b, "b1");
        b1.start();
        Thread b2 = new Thread(b, "b2");
        b2.start();
        try {
            b1.join();
            b2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

What I expect is, for the instance of A contained by b , count to increment from 0 to 8, sequentially. 我期望的是,对于b包含的A实例, count从0到8依次递增。

But for this code: 但是对于此代码:

synchronized public A incrementCount() {
     return new A(count + 1);
}

synchronized public void doStuff(long i) {
    try {
        Thread.sleep(i * 100);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Or this code (equivalent to above I think): 或这段代码(相当于我认为的上述代码):

public A incrementCount() {
    synchronized (this) {
        return new A(count + 1);
    }
}

public void doStuff(long i) {
    synchronized (this){
        try {
            Thread.sleep(i * 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I get results like this: 我得到这样的结果:

THREAD|    OBJECT     |COUNT
----------------------------
main  |testbed.A@11121|  0    
b1    |testbed.A@64f6c|  1    
b1    |testbed.A@87238|  2    
b2    |testbed.A@2bb51|  2    
b2    |testbed.A@17d5d|  3    
b1    |testbed.A@16fa4|  4    
b2    |testbed.A@95c08|  4    
b1    |testbed.A@191d8|  5    
b2    |testbed.A@2d9c0|  5    

Clearly, something is amiss. 显然,有些不对劲。 I also think it is worth noting that the objects all appear to be unique objects, even though there are duplicate numbers. 我也认为值得注意的是,即使有重复的数字,这些对象看起来都是唯一的对象。

But for this code (in Class A): 但是对于此代码(在A类中):

private final Object incrementLock = new Object();
private final Object doStuffLock = new Object();

...

public A incrementCount() {
    synchronized (incrementLock) {
        return new A(count + 1);
    }
}

public void doStuff(long i) {
    synchronized (doStuffLock){
        try {
            Thread.sleep(i * 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Or this code (in Class B): 或此代码(在B类中):

@Override
synchronized public void run() {
    for (int i = 1; i < 5; i++) {
        a.doStuff(i);
        a = a.incrementCount();
    }
}

I get the results I expect: 我得到了我期望的结果:

THREAD|    OBJECT       |COUNT
------------------------------
main  |testbed.A@f7f540 |  0    
b1    |testbed.A@64f6cd |  1    
b2    |testbed.A@872380 |  2    
b1    |testbed.A@2bb514 |  3    
b2    |testbed.A@17d5d2a|  4    
b1    |testbed.A@16fa474|  5    
b2    |testbed.A@95c083 |  6    
b1    |testbed.A@191d8c1|  7    
b2    |testbed.A@2d9c06 |  8

Since there is only a single object being accessed by both threads ( b1 and b2 ), why aren't synchronized (this) or synchronized public... locking the object instance, preventing both threads from entering the synchronized blocks and corrupting count , so to speak? 由于两个线程( b1b2 )仅访问一个对象,所以为什么不synchronized (this)synchronized public...锁定对象实例,防止两个线程进入同步块并破坏count ,因此说话? Or did I miss something? 还是我错过了什么?

You should synchronize the code in B , where you have multiple threads mutating the state (instance variable a ). 您应该同步B的代码,在B ,有多个线程使状态发生变化(实例变量a )。 It does not make sense to synchronize methods in A , because instances of the class are really just immutable value objects. 同步A方法没有任何意义,因为类的实例实际上只是不可变的值对象。

When synchronizing methods on this in A , the most problematic part in the code is this: Athis同步方法时,代码中最有问题的部分是:

a = a.incrementCount();

because there you leak the monitor outside of the class and re-assign the variable that's holding it. 因为在那里您将监视器泄漏到类之外,然后重新分配保存它的变量。

Even though the version of A that uses different monitor objects for both methods seems to work, there is a race condition (which you could see if you add more threads and iteration steps and reduce/eliminate the sleep time in doStuff() ) because nothing guarantees that the correctly incremented a got assigned in the above code. 即使使用两种方法的监视器对象都不同的A版本也可以正常工作,但还是存在竞争条件(您可以看到是否添加了更多线程和迭代步骤并减少/消除了doStuff()的睡眠时间),因为什么都没有保证以上代码中分配的a正确递增。

The only way to make your code thread-safe is to synchronize the run() method in B . 使代码具有线程安全性的唯一方法是同步Brun()方法。

In incrementCount() you create each time a new instance of A which pretty much compromises your whole idea of synchronization. incrementCount() ,每次创建一个新的A实例时,这都会损害整个同步概念。 This is your problem here. 这是你的问题。 Just increment the counter, don't replace/recreate the A instance each time. 只需增加计数器,不要每次都替换/重新创建A实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM