我正在尝试使用Threads并且它没有按预期工作,尽管我已经应用了同步。
我有一个方法
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();
}
}
Homer.java
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);
}
}
*注意:是的我知道荷马可能会吃掉负数量的饼干,这可能是也可能是不可能的。
答案 0 :(得分:2)
问题是这些行:
System.out.println(amount + " cookies were Put");
System.out.println(amount + " cookies were Removed");
jar
上未同步。如果您只查看{em> 同步的xThread
和Cookies: N
输出,那么这些输出始终是正确排序的。
答案 1 :(得分:2)
您还应该synchronize
使用getCookie
和putCookie
方法,否则它们会与printStatus
方法交错。
答案 2 :(得分:1)
好的,这是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上同步具有相同的效果,但它具有锁变量为private
的优点。这样,我知道没有其他程序员会因为其他原因而在我的锁上同步。 (由于任何其他原因,他们必须非常疯狂地在System.out上进行同步,但那时是一些疯狂的开发人员。)
另请注意:我使consoleLock静态,因为System.out是静态的。只有一个System.out,所以我只有一个锁对象很重要。