同步应该发生在成员 class 还是包装器 class 中?
Should synchronization happen in the member class or in the wrapper class?
首先,我读过的内容:
- In Java critical sections, what should I synchronize on?
- synchronized(this) blocks whole object? [duplicate]
- Does thread.yield() lose the lock on object if called inside a synchronized method?
- What is the difference between a synchronized method and synchronized block in Java?
- Java synchronized difference threading [duplicate]
- What is the difference between a synchronized function and synchronized block?
我点击了无数链接,指向大多数帖子中列出的重复项。所以如果这是重复的,我提前道歉。我觉得我的问题没有被这些或后续链接中的任何一个回答。但话又说回来,我现在问是因为我不知道这里发生了什么。现在进入主要活动...
我有一对 classes,A
和 B
。 Class B
有一个 A
的实例作为成员:
Class答:
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乙:
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();
}
}
}
我有一个 class 获取 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();
}
}
}
我期望的是,对于 b
包含的 A 实例,count
从 0 依次递增到 8。
但是对于这段代码:
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();
}
}
或者这段代码(我认为相当于上面的代码):
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();
}
}
}
我得到这样的结果:
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
显然,有些不对劲。我还认为值得注意的是,即使有重复的数字,这些对象看起来都是唯一的对象。
但对于此代码(在 Class 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();
}
}
}
或此代码(在 Class B 中):
@Override
synchronized public void run() {
for (int i = 1; i < 5; i++) {
a.doStuff(i);
a = a.incrementCount();
}
}
我得到了我期望的结果:
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
既然只有一个对象被两个线程(b1
和 b2
)访问,为什么 synchronized (this)
或 synchronized public...
不锁定对象实例,防止两个线程进入同步块并破坏count
,可以这么说吗?还是我漏掉了什么?
在 incrementCount()
中,您每次都会创建一个 A
的新实例,这几乎会损害您的整个同步想法。这是你的问题。只需增加计数器,不要每次都 replace/recreate A
实例。
您应该同步 B
中的代码,其中有多个线程改变状态(实例变量 a
)。在 A
中同步方法没有意义,因为 class 的实例实际上只是不可变的值对象。
在 A
中同步 this
上的方法时,代码中最有问题的部分是:
a = a.incrementCount();
因为你在 class 之外泄漏了监视器并重新分配了持有它的变量。
尽管 A
的版本对两种方法使用不同的监视器对象似乎都有效,但存在竞争条件(如果您添加更多线程和迭代步骤和 reduce/eliminate doStuff()
) 中的休眠时间,因为没有任何东西可以保证在上面的代码中分配了正确递增的 a
。
使代码线程安全的唯一方法是同步 B
中的 run()
方法。
首先,我读过的内容:
- In Java critical sections, what should I synchronize on?
- synchronized(this) blocks whole object? [duplicate]
- Does thread.yield() lose the lock on object if called inside a synchronized method?
- What is the difference between a synchronized method and synchronized block in Java?
- Java synchronized difference threading [duplicate]
- What is the difference between a synchronized function and synchronized block?
我点击了无数链接,指向大多数帖子中列出的重复项。所以如果这是重复的,我提前道歉。我觉得我的问题没有被这些或后续链接中的任何一个回答。但话又说回来,我现在问是因为我不知道这里发生了什么。现在进入主要活动...
我有一对 classes,A
和 B
。 Class B
有一个 A
的实例作为成员:
Class答:
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乙:
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();
}
}
}
我有一个 class 获取 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();
}
}
}
我期望的是,对于 b
包含的 A 实例,count
从 0 依次递增到 8。
但是对于这段代码:
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();
}
}
或者这段代码(我认为相当于上面的代码):
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();
}
}
}
我得到这样的结果:
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
显然,有些不对劲。我还认为值得注意的是,即使有重复的数字,这些对象看起来都是唯一的对象。
但对于此代码(在 Class 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();
}
}
}
或此代码(在 Class B 中):
@Override
synchronized public void run() {
for (int i = 1; i < 5; i++) {
a.doStuff(i);
a = a.incrementCount();
}
}
我得到了我期望的结果:
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
既然只有一个对象被两个线程(b1
和 b2
)访问,为什么 synchronized (this)
或 synchronized public...
不锁定对象实例,防止两个线程进入同步块并破坏count
,可以这么说吗?还是我漏掉了什么?
在 incrementCount()
中,您每次都会创建一个 A
的新实例,这几乎会损害您的整个同步想法。这是你的问题。只需增加计数器,不要每次都 replace/recreate A
实例。
您应该同步 B
中的代码,其中有多个线程改变状态(实例变量 a
)。在 A
中同步方法没有意义,因为 class 的实例实际上只是不可变的值对象。
在 A
中同步 this
上的方法时,代码中最有问题的部分是:
a = a.incrementCount();
因为你在 class 之外泄漏了监视器并重新分配了持有它的变量。
尽管 A
的版本对两种方法使用不同的监视器对象似乎都有效,但存在竞争条件(如果您添加更多线程和迭代步骤和 reduce/eliminate doStuff()
) 中的休眠时间,因为没有任何东西可以保证在上面的代码中分配了正确递增的 a
。
使代码线程安全的唯一方法是同步 B
中的 run()
方法。