簡體   English   中英

從非同步方法內部調用同步

[英]Calling a synchronized from inside a non-synchronized method

我正在嘗試使用Threads,盡管我已經應用了Synchronization,但它並未按預期工作。

我有辦法

private synchronized void printStatus(){
        System.out.println("\t\t\t" + Thread.currentThread().getName());
        System.out.println("\t\t\tCookies: " + contents);
}

所以我期望,無論何時調用,在輸出中我都會得到一行接一行。 但是,事實並非如此。 我的輸出如下所示:

            homerThread
            Cookies: 0
            margeThread
0 cookies were Removed
            Cookies: 0
            homerThread
3 cookies were Put
            Cookies: 3
0 cookies were Removed
            margeThread
            Cookies: 3
3 cookies were Put
            homerThread
            Cookies: 6
4 cookies were Removed
            margeThread
            Cookies: 2
1 cookies were Put
            homerThread
            Cookies: 3
3 cookies were Removed
                margeThread
                Cookies: 0
....

如您所見,很多行未正確同步

為什么會這樣呢?


為了完整起見,我在下面包括了我的整個代碼;

Main.java類

public class Main {
    public static void main(String[] args) { 

        CookieJar jar = new CookieJar(); 

        Homer homer = new Homer(jar); 
        Marge marge = new Marge(jar); 

        Thread homerThread = new Thread(homer);
        homerThread.setName("homerThread");
        Thread margeThread = new Thread(marge);
        margeThread.setName("margeThread");

        homerThread.start();
        margeThread.start();
    } 

}

荷馬

import java.util.Random;

public class Homer implements Runnable {

    CookieJar jar; 

    public Homer(CookieJar jar) { 
        this.jar = jar; 
    } 
    public void eat(int amnt) { 
        jar.getCookie(amnt); 
    } 
    public void run() { 
        Random random = new Random();
        for(int i = 0; i < 10; i++){
            eat(random.nextInt(5));
        } 
    } 
}

瑪格

import java.util.Random;

public class Marge implements Runnable {
    CookieJar jar; 
    public Marge(CookieJar jar) { 
        this.jar = jar; 
    } 
    public void bake(int cookie) { 
        jar.putCookie(cookie); 
    } 
    public void run() { 
        Random random = new Random();
        for(int i = 0; i < 10; i++){
            bake(random.nextInt(5));
        }
    } 


}

CookieJar.java

public class CookieJar {
    int contents = 0; 
    boolean hasCookie = false; 

    public void putCookie(int amount) {  
        printStatus();
        contents += amount;
        System.out.println(amount + " cookies were Put");
    } 

    public void getCookie(int amount) { 
        printStatus();
        contents -= amount;
        System.out.println(amount + " cookies were Removed");
    }

    private synchronized void printStatus(){
        System.out.println("\t\t\t" + Thread.currentThread().getName());
        System.out.println("\t\t\tCookies: " + contents);
    }
}

*注意:是的,我知道荷馬可能最終會吃掉少量的餅干,這可能會或可能不會。

問題是這些行:

System.out.println(amount + " cookies were Put");

System.out.println(amount + " cookies were Removed");

jar上未同步。 如果僅查看xThreadCookies: N輸出,它們同步的,則它們總是正確排序的。

您還應該synchronize getCookieputCookie方法,否則它們將與printStatus方法交錯。

好的,這是synchronized作用:它防止兩個或多個線程同時在同一個對象上同步。 它沒有做任何其他事情。 這不會阻止兩個線程同時進入相同的同步方法(這些線程可能在不同的實例上調用相同的方法)。 在對象上同步不會阻止其他線程修改該對象。 (其他線程可能處於非同步方法中)。

如果要防止其他線程在printStatus()打印的兩行之間打印消息,則僅同步printStatus()是不夠的。 您必須同步可以使用System.out 每個線程。 這是我的處理方式:

private void printStatus() {
    synchronized (System.out) {
        System.out.println("\t\t\t" + Thread.currentThread().getName());
        System.out.println("\t\t\tCookies: " + contents);
    }
}

public void putCookie(int amount) {  
    printStatus();
    contents += amount;
    synchronized (System.out) {
        System.out.println(amount + " cookies were Put");
    }
}

...

我在這里在System.out上進行同步以說明問題。 System.out是我要保護的東西,如果程序中嘗試寫入System.out的每個方法都是從synchronized (System.out)內部synchronized (System.out)這樣做的,那么程序中還有其他線程和其他同步對象也沒關系。 ,則輸出將全部正確交織。


額外信用:

在生產代碼中,我應該這樣做:

private static Object consoleLock = new Object();

...
    synchronized (consoleLock) {
        System.out.println(...);
        ...
    }
...

只要我在所有地方都始終使用consoleLock ,它將與在System.out上同步具有相同的效果,但是它具有lock變量為private的優點。 這樣,我知道沒有其他程序員會因為其他原因而在我的鎖上進行同步。 (出於任何其他原因,他們必須非常瘋狂才能在System.out上進行同步,但是那里還有一些瘋狂的開發人員。)

另請注意:因為System.out是靜態的,所以我將consoleLock設置為靜態。 只有一個System.out,所以只有一個鎖對象很重要。

暫無
暫無

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

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