public class counting
{
private static int counter = 0;
public void boolean counterCheck(){
counter++;
if(counter==10)
counter=0;
}
}
方法counterCheck可以由我的应用程序中的多个线程访问。我知道静态变量不是线程安全的。如果有人可以通过示例帮助我,或者告诉我为什么必须同步方法或阻止,我将不胜感激。如果我不同步会发生什么?
答案 0 :(得分:6)
显然不是线程安全的。考虑两个完全并行运行的线程。如果计数器是9,它们都会递增计数器,导致计数器为11.然后它们都不会看到计数器等于10,所以计数器将从那时开始递增,而不是按预期包装。
答案 1 :(得分:2)
这不是线程安全的, AND 这种从多个线程更新计数的模式可能是实现负扩展(在添加更多线程时运行速度较慢)的第一种方式线程应用程序。
如果你添加必要的锁定以使这个线程安全,那么每个线程将在计数时完全停止。即使您使用原子操作来更新计数器,最终也会在更新计数器的每个线程之间弹出CPU缓存行。
现在,如果每个线程操作在更新计数器之前需要相当长的时间,则这不是问题。但是如果每个操作都很快,计数器更新将序列化操作,导致所有线程上的一切都变慢。
答案 2 :(得分:0)
最大的危险?在counter
检查之前,counter == 10
两次递增,使重置为0
永远不会发生。
答案 3 :(得分:0)
想象一下counter
是9。
线程1执行此操作:
counter++; // counter = 10
线程2执行此操作:
counter++; // counter = 11
if(counter==10) // oops
现在,您可能认为可以通过以下方式解决此问题:
if(counter >= 10) counter -= 10;
但是现在,如果两个线程检查条件并发现它是真的会发生什么,那么两个线程都会将计数器递减10(现在你的计数器是负数)。
或者在更低级别,counter++
实际上是三个操作:
counter
counter
counter
所以:
在这种情况下,您希望计数器增加两次,但它只增加一次。您可以想象它好像正在执行此代码:
c1 = counter;
c2 = counter;
c1 = c1 + 1;
c2 = c2 + 1;
counter = c1; // Note that this has no effect since the next statement overrides it
counter = c2;
因此,您可以将其包装在synchronized
块中,但如果您只有几个线程,则使用AtomicInteger会更好:
public class counting {
private static AtomicInteger counter = new AtomicInteger(0);
public static void counterCheck() {
int value = counter.incrementAndGet();
// Note: This could loop for a very long time if there's a lot of threads
while(value >= 10 && !counter.compareAndSet(value, value - 10)) {
value = counter.get();
}
}
}
答案 4 :(得分:0)
由于多种原因,它不是线程安全的。最明显的一个是你可以有两个线程从9到11,正如其他答案所提到的那样。
但是由于counter ++不是原子操作,你也可以让两个线程读取相同的值并在之后递增到相同的值。 (意思是两个调用实际上只增加1)。
或者你可以让一个线程进行多次修改,而另一个线程总是看到0,因为由于Java内存模型,另一个线程可能会看到一个缓存在寄存器中的值。
良好的经验法则:每次多个线程访问某些共享状态,并且其中一个易于修改此共享状态时,所有访问,甚至是只读访问都必须使用相同的锁同步。
答案 5 :(得分:0)
counter++
中的第一个本身并非线程安全
硬件限制使其等同于
int tmp = counter;
tmp=tmp+1;
counter=tmp;
当两个线程同时存在时会发生什么?一个更新丢失,这是什么
你可以使用atomicInteger和CAS循环使这个线程安全
private static AtomicInteger counter = new AtomicInteger(0);
public static boolean counterCheck(){
do{
int old = counter.get();
int tmp = old+1;
if(tmp==10)
tmp=0;
}
}while(!counter.compareAndSet(old,tmp));
}