簡體   English   中英

同步應該在成員類中還是在包裝器類中發生?

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

首先,我已經閱讀了以下內容:

而且,我跟蹤了無數鏈接,這些鏈接都指向大多數這些帖子中列出的重復項。 因此,如果這是重復的話,我提前致歉。 我覺得這些問題或后續鏈接都沒有回答我的問題。 但是話又說回來,我現在問是因為我對這里發生的事情一無所知。 現在開始主要活動...

我有一對班, 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...鎖定對象實例,防止兩個線程進入同步塊並破壞count ,因此說話? 還是我錯過了什么?

您應該同步B的代碼,在B ,有多個線程使狀態發生變化(實例變量a )。 同步A方法沒有任何意義,因為類的實例實際上只是不可變的值對象。

Athis同步方法時,代碼中最有問題的部分是:

a = a.incrementCount();

因為在那里您將監視器泄漏到類之外,然后重新分配保存它的變量。

即使使用兩種方法的監視器對象都不同的A版本也可以正常工作,但還是存在競爭條件(您可以看到是否添加了更多線程和迭代步驟並減少/消除了doStuff()的睡眠時間),因為什么都沒有保證以上代碼中分配的a正確遞增。

使代碼具有線程安全性的唯一方法是同步Brun()方法。

incrementCount() ,每次創建一個新的A實例時,這都會損害整個同步概念。 這是你的問題。 只需增加計數器,不要每次都替換/重新創建A實例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM