我是Java中多线程的新手,我制作了一些代码来了解它是如何工作的。我有global = 0
作为全局int变量,并且使用for
循环我初始化了很多线程(100)以将1添加到我的全局变量中。
在代码的最后,结果应为100,但不是。我有时在代码末尾99或任何其他数字(大约100)。所以我的问题是,为什么线程"战斗"他们之间并没有把这笔钱合适吗?
public class test extends Thread {
public static int global =0;
public static void main(String[] args) throws Exception {
for(int i=0;i<100;i++){
String stream = String.valueOf(i);
new test2(stream).start();
}
Thread.sleep(1000);
System.out.println(global);
}
public test(String str) {
super(str);
}
public void run() {
int a = Integer.parseInt(getName());
global = global+1;
System.out.println("El hilo "+a+" tiene el número "+global);
}
}
我知道我不需要int a = Integer.parseInt(getName());
,但我假装你将来使用这个名字。并且,如果我现在删除它,结果是错误的。
答案 0 :(得分:13)
这是一种典型的竞争条件。
您的一个主题,称为“A”,已读取值global
,说10
,并向其添加1
,但它尚未存储值{ {1}}返回11
。
你的另一个主题,称之为“B”,现在正在读取global
,global
的“旧”值,它正在向其添加10
,并存储该值1
返回11
。
然后,最后允许线程“A”将其值global
存储回11
。已经发生了两次增量,但最终结果是只有一次增量有效发生。
这是因为递增值并将其存储回变量的操作不是 atomic 。这意味着有多个单独的操作,如果被中断,可能会产生不正确的结果。
您必须创建一个global
块以强制执行原子操作。您可以锁定类对象本身。
synchronized
作为替代方案,您可以将synchronized (test.class) {
global = global+1;
}
变量设为AtomicInteger
,为您处理原子更新。
答案 1 :(得分:7)
那是因为
global = global+1;
不是原子操作。它包含在
中因此,由于线程并发执行,您可能会遇到竞争条件:
多线程就是要确保正确修改共享状态。您需要同步对全局变量的每次访问以使其更改为原子。另一种方法是使用AtomicInteger,它允许以原子方式递增。
答案 2 :(得分:3)
以下语句不是线程安全的:
global = global+1;
想象一下这种情况:
线程#1到达此声明。它的内容为global
0
。此时,线程#2到达相同的语句。它会显示global
,但仍为0
。现在,线程#1将1加0并将{1}}中的总数1保存。线程#2继续,添加1到0并将总计1保存到global
。
答案 3 :(得分:1)
为了使线程安全,最简单的方法是同步访问&#34; global&#34;通过某事。例如,您的类本身(也是Object)
public class test extends Thread {
public static int global = 0;
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100; i++) {
String stream = String.valueOf(i);
new test(stream).start();
}
Thread.sleep(1000);
System.out.println(global);
}
public test(String str) {
super(str);
}
public void run() {
int a = Integer.parseInt(getName());
synchronized (test.class) {
global = global + 1;
}
System.out.println("El hilo " + a + " tiene el número " + global);
}
}