[英]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
上未同步。 如果僅查看xThread
和Cookies: N
輸出,它們是同步的,則它們總是正確排序的。
您還應該synchronize
getCookie
和putCookie
方法,否則它們將與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.