[英]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
. 我有一对班, A
和B
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? 由于两个线程( b1
和b2
)仅访问一个对象,所以为什么不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: 在A
与this
同步方法时,代码中最有问题的部分是:
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
. 使代码具有线程安全性的唯一方法是同步B
的run()
方法。
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.