我正在试验线程,尽管我已经应用了同步,但是它没有按预期工作。
我有办法
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,所以只有一个锁对象很重要。