线程安全的计数器

java puzzler

 

不能通过编译的例子。

Creature creature = new Creature();是一个局部变量声明语句(local variable declaration statement) JLS 4.4,java语言规范不允许一个局部变量声明语句作为一条语句在循环中重复执行。一个本地变量声明作为一条只能直接出现在一个语句块中。(一个语句块又括号以及其中包括的语句和声明构成)

 

package arkblue.lang.javapuzzler.n55;

public class Creator {
	public static void main(String[] args) {
	for (int i = 0; i < 100; i++)
		// 不能通过编译
	Creature creature = new Creature();
	System.out.println(Creature.numCreated());
	}
}

class Creature {
	private static long numCreated = 0;

	public Creature() {
		numCreated++;
	}

	public static long numCreated() {
		return numCreated;
	}
}

 

 

修改后:

  

package arkblue.lang.javapuzzler.n55;

public class Creator {
	public static void main(String[] args) {
	for (int i = 0; i < 100; i++) {
		Creature creature = new Creature();
	}
			
	System.out.println(Creature.numCreated());
	}
}

class Creature {
	private static long numCreated = 0;

	public Creature() {
		numCreated++;
	}

	public static long numCreated() {
		return numCreated;
	}
}

 

但是上面的代码不是线程安全的,使用synchronized关键字后的效果:

 

package arkblue.lang.javapuzzler.n55;

public class Creator {
	public static void main(String[] args) {
	for (int i = 0; i < 100; i++) {
		Creature creature = new Creature();
	}
			
	System.out.println(Creature.numCreated());
	}
}

class Creature {
	private static long numCreated;
		public Creature() {
			synchronized (Creature.class) {
				numCreated++;
			}
		}
		
	public static synchronized long numCreated() {
		return numCreated;
	}
}

 

 

在5.0后,可以使用AtomicLong实例,他在面临并发的时候绕过对同步的需求。

 

package arkblue.lang.javapuzzler.n55;

import java.util.concurrent.atomic.AtomicLong;

public class Creator {
	public static void main(String[] args) {
	for (int i = 0; i < 100; i++) {
		@SuppressWarnings("unused")
		Creature creature = new Creature();
	}
			
	System.out.println(Creature.numCreated());
	}
}

class Creature {
	private static AtomicLong numCreated = new AtomicLong();
	public Creature() {
	numCreated.incrementAndGet();
	}
	public static long numCreated() {
	return numCreated.get();
	}
}

 

结论

(1)一个本地变量声明语句不能用在循环中的重复执行语句,他作为一条语句只能出现在语句块中。

(2)使用变量作为计数器,要使用long,而不是int,以防止溢出。

(3)在多线程环境下,要么对计数器同步,要么使用AutomicLong。

你可能感兴趣的:(多线程)