[英]Java: Multiple threads decrementing static variable via synchronized method accessing variable at the same time
我正在嘗試同步我的Person
類方法,以便我的靜態計數器變量一次減少一個線程。
public class Person extends Thread {
private static int count = 10;
public void decrement() {
synchronized(Person.class) {
count--;
}
}
public int getCount() {
return count;
}
public void run(){
while( count > 0){
this.decrement();
System.out.print(this.getCount() + ",");
}
}
}
這是我的主要課程。 每個線程將通過synchronized方法遞減到靜態計數器,以避免多個線程訪問相同的資源。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
Person p4 = new Person();
Person p5 = new Person();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
}
}
但是當我運行我的程序時,它正在打印重復的計數器值。 我究竟做錯了什么?
輸出:
8,8,7,6,5,4,3,2,1,0
8,7,5,3,1,0,6,8,4,0
原始代碼中發生的事情如下:
由於您鎖定了減量而不是減量和輸出,因此它似乎會減少多次。
換句話說,無法保證此代碼將背靠背執行:
this.decrement();
System.out.print(this.getCount() + ",");
這是固定代碼。 它會在遞減時返回當前計數值,以便可以返回並打印新值。
public class Person extends Thread {
private static int count = 10;
public int decrement() {
synchronized(Person.class) {
count = count - 1;
return count;
}
}
public int getCount() {
synchronized(Person.class) {
return count;
}
}
public void run(){
while( getCount() > 0){
int count = this.decrement();
System.out.println(count);
}
}
}
我建議使用AtomicInteger執行此任務:
import java.util.concurrent.atomic.AtomicInteger;
public class Person extends Thread {
private static AtomicInteger count = new AtomicInteger(10);
public int decrement() {
return count.decrementAndGet();
}
public void run(){
while(count.get() > 0){
int currentCount = this.decrement();
System.out.print(currentCount + ",");
}
}
}
僅僅同步寫入是不夠的,您還必須同步getter(否則讀取器線程可能會讀取過時的值)。 但在這種情況下,問題是其他線程可以在線程遞減的時間和同一線程檢索值的時間之間交錯執行。
使用java.util.concurrent.atomic.AtomicInteger存儲計數。 但是如果你為減量和獲取保持不同的方法(鎖定減量並分別鎖定getter),仍然沒有什么可以保證線程不會以導致重復寫出的方式進行交錯。 使用AtomicInteger的decrementAndGet方法可確保遞減的值是返回的值。
正確的同步是關鍵,但使用AtomicInteger
並不是完整的答案。 你需要意識到的是,每個線程需要報告剛遞減的計數,即使你是1)使用AtomicInteger
或2)正確(單獨)同步decrement
和getCount
方法,也可以通過另一個線程進行更改。
while
循環的主體是一個關鍵部分 ,一段不能被中斷的代碼。
public void run(){
while( count > 0){
synchronized (Person.class)
{
decrement();
System.out.print(getCount() + ",");
}
}
}
輸出:
9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4
有時它會在-3
停止。 現在,每個實例都可以自由地繼續進行和減少,因為它檢查通過的while
條件,然后線程被中斷,然后另一個線程遞減。 然后原始線程減少,即使它已經降到0
! 檢查循環內部。
public void run(){
while( count > 0){
synchronized (Person.class)
{
if (count > 0)
{
decrement();
System.out.print(getCount() + ",");
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.