從1數(shù)到1000诅愚,有如下類:
class Calculate {
static int count = 0
def static inc() {
try {
Thread.sleep(1)
} catch (Exception e) {
}
count++
println count
}
}
開始數(shù):
class Test {
public static void main(String[] args) {
for (int i in 1..1000) {
new Thread(new Runnable() {
@Override
void run() {
Calculate.inc()
}
}).start()
}
}
}
結(jié)果很接近但并不是預(yù)想的1000.
這要從jvm運(yùn)行時(shí)刻的內(nèi)存分配來解釋這個(gè)問題。每個(gè)線程運(yùn)行時(shí)都有一個(gè)線程棧臭家,存放線程運(yùn)行時(shí)的變量种蘸,當(dāng)線程需要訪問某一個(gè)對象時(shí),會(huì)通過引用找到對應(yīng)的堆內(nèi)存地址媚创,并把對應(yīng)堆內(nèi)存中的值加載到線程工作內(nèi)存中渗钉,建立一個(gè)堆內(nèi)存中的值的副本,在線程修改完值之后會(huì)再把副本寫回到對象的堆內(nèi)存中钞钙。
由于讀取寫回等操作并不是原子操作晌姚,當(dāng)主內(nèi)存的值發(fā)生改變時(shí)粤剧,并不會(huì)影響線程工作內(nèi)存中的值,所以計(jì)算結(jié)果和預(yù)期并不一樣挥唠。
這時(shí)候就要對自增操作進(jìn)行加鎖抵恋。
class Calculate {
static int count = 0
def static inc() {
synchronized (Calculate.class) {
try {
Thread.sleep(1)
} catch (Exception e) {
}
count++
println count
}
}
}
開始數(shù):
class Test {
public static void main(String[] args) {
for (int i in 1..1000) {
new Thread(new Runnable() {
@Override
void run() {
Calculate.inc()
}
}).start()
}
}
}
還有volatile
修飾的變量并不能保證在并發(fā)情況的正確性。jvm虛擬機(jī)只能保證從主內(nèi)存加載到線程工作內(nèi)存中的值是最新的宝磨。
不加鎖static volatile int count = 0
這樣解決不了問題弧关。