[英]Should synchronization happen in the member class or in the wrapper class?
首先,我已經閱讀了以下內容:
而且,我跟蹤了無數鏈接,這些鏈接都指向大多數這些帖子中列出的重復項。 因此,如果這是重復的話,我提前致歉。 我覺得這些問題或后續鏈接都沒有回答我的問題。 但是話又說回來,我現在問是因為我對這里發生的事情一無所知。 現在開始主要活動...
我有一對班, A
和B
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
由於兩個線程( b1
和b2
)僅訪問一個對象,所以為什么不synchronized (this)
或synchronized public...
鎖定對象實例,防止兩個線程進入同步塊並破壞count
,因此說話? 還是我錯過了什么?
您應該同步B
的代碼,在B
,有多個線程使狀態發生變化(實例變量a
)。 同步A
方法沒有任何意義,因為類的實例實際上只是不可變的值對象。
在A
與this
同步方法時,代碼中最有問題的部分是:
a = a.incrementCount();
因為在那里您將監視器泄漏到類之外,然后重新分配保存它的變量。
即使使用兩種方法的監視器對象都不同的A
版本也可以正常工作,但還是存在競爭條件(您可以看到是否添加了更多線程和迭代步驟並減少/消除了doStuff()
的睡眠時間),因為什么都沒有保證以上代碼中分配的a
正確遞增。
使代碼具有線程安全性的唯一方法是同步B
的run()
方法。
在incrementCount()
,每次創建一個新的A
實例時,這都會損害整個同步概念。 這是你的問題。 只需增加計數器,不要每次都替換/重新創建A
實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.