gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-04 06:07:24 25 4
gpt4 key购买 nike

首先,我读过的内容:

我跟踪了大多数帖子中列出的无数重复链接。因此,如果这是重复的,我提前道歉。我认为其中任何一个或后续链接都没有回答我的问题。但话又说回来,我现在问是因为我不知道这里发生了什么。现在进入主要 Activity ...

我有一对类,AB。类 B 有一个 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();
}
}
}

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

我有一个类,它采用 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

显然,有些不对劲。我还认为值得注意的是,即使存在重复的数字,这些对象似乎都是唯一的对象。

但是对于这段代码(在 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();
}
}
}

或者这段代码(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

既然两个线程(b1b2)只访问一个对象,为什么不 synchronized (this)synchronized public... 锁定对象实例,防止两个线程进入同步块(synchronized block)并破坏 count 呢?还是我错过了什么?

最佳答案

您应该同步 B 中的代码,其中有多个线程改变状态(实例变量 a)。同步 A 中的方法没有意义,因为类的实例实际上只是不可变的值对象。

在同步Athis上的方法时,代码中最有问题的部分是:

a = a.incrementCount();

因为您将监视器泄漏到类之外并重新分配保存它的变量。

尽管对两种方法使用不同监视器对象的 A 版本似乎都可以工作,但存在竞争条件(如果您添加更多线程和迭代步骤并减少/消除 doStuff() 中的 sleep 时间,您可以看到这种情况),因为没有任何东西可以保证在上面的代码中分配正确递增的 a

使代码线程安全的唯一方法是同步 B 中的 run() 方法。

关于java - 同步应该发生在成员类中还是包装类中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29134351/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com