我正在试验线程,尽管我已经应用了同步,但是它没有按预期工作。

我有办法

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


*注意:是的,我知道荷马最终可能会吃掉少量的饼干,这可能会或可能不会。

最佳答案

好的,这是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)内部执行的,则输出将全部正确交织。



额外信用:

在生产代码中,我应该这样做:

private static Object consoleLock = new Object();

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


只要我在任何地方都始终使用consoleLock,它将与在System.out上同步具有相同的效果,但是它的优点是lock变量是private。这样,我知道没有其他程序员会因为其他原因而在我的锁上进行同步。 (由于任何其他原因,他们必须非常疯狂才能在System.out上进行同步,但是那里还有一些疯狂的开发人员。)

另请注意:因为System.out是静态的,所以我将consoleLock设置为静态。只有一个System.out,所以只有一个锁对象很重要。

09-11 03:53
查看更多