4 回答
TA贡献1829条经验 获得超4个赞
您的代码存在两个并发问题。
对于并发任务,共享 int
i
既没有适当声明也没有访问。它至少需要是易失性的,但为了正确处理原子增量应该是一个AtomicInteger
. Volatile 将有助于保证其他线程可以看到值是否发生变化(JVM 无法保证这一点),但原子整数更进一步,它提供了原子操作,这才是您真正需要的。您的循环可以同时被两个线程访问,因为条件检查与变量的增量是分开的。要正确处理运行循环的并发线程,您需要
while
此处的循环条件也将增量作为单个原子操作执行。或者你需要添加一个同步块。
也许尝试类似下面未经测试的代码:
public class Counter {
private final AtomicInteger i = new AtomicInteger(1);
public void add() {
for (;;)
{
int localCount = i.getAndIncrement();
if (localCount > 5) { break; }
System.out.println(localCount + " " + Thread.currentThread().getName());
}
}
}
逐行解释一下上面的代码:
我们切换
i
到 anAtomicInteger
,因为它允许我们获取当前值并自动将下一个值加一。这种原子性很重要,因为否则两个竞争线程可能会增加到相同的值,而不是在所有线程中读取和增加每个值恰好一次。我们可以通过将代码的递增部分放入同步块中来实现相同的目的。我们切换到一个
for (;;)
循环,以便我们可以将循环控制/终止代码移动到循环体内。这使我们能够轻松地将原子增量的结果用于控制代码和循环体内(例如在语句中println
)。i.getAndIncrement()
原子地增加我们的计数器并返回给我们增加之前的值。无论有多少线程在运行,都可以保证此操作有效,并确保我们不会遗漏或重复任何数字。将先前的计数值分配给localCount
int 为我们提供了在循环中使用的值。如果我们尝试i
在循环内重新访问而不是使用 localCount,那么我们最终会在某个时刻得到不同的数字,因为其他线程正在同时递增i
。我们检查
localCount
它是否超出了我们的终止限制,如果超过了,我们就中断循环。我们不检查,i
因为它可能已被另一个线程修改,并且我们只关心该线程当前的数量是否超出限制。最后,我们使用
localCount
instead of 来执行 printlni
,因为i
在代码到达此处时可能已被另一个线程修改(这将导致打印重复行)。
请注意,此代码不保证计数器的数字将按顺序打印。 很可能大多数时候都是这种情况,但肯定不能保证也不应该依赖它。您的问题没有说明实际的预期/要求的结果,如果这是一项要求,那么您很可能需要围绕增量和 println 语句进行同步,以确保不会乱序执行。
例如同步解决方案:
public class Counter {
int i = 1;
public void add() {
for (;;) {
synchronized(this) {
if (i > 5) { break; }
System.out.println(i + " " + Thread.currentThread().getName());
i++;
}
}
}
}
TA贡献1966条经验 获得超4个赞
这是个简单的。
您创建了一个名为
c1
;的计数器您从此计数器创建了一个 Runnable;
您将同一个可运行对象传递给了两个线程。两个线程现在将在同一个 Counter 对象上运行
c1
;您在方法内运行 while 循环
add()
。
该add()
方法在一行上打印一些内容,并在下一行上递增i
。
对于您当前的输出,在第一个线程执行其 print 语句后,它被中断,第二个线程获得了 CPU 的控制权。第二个线程设法执行了它的打印。然后任一线程递增计数器(我们不知道是哪一个),并且打印和计数器递增两个步骤继续。
TA贡献1859条经验 获得超6个赞
问题是您正在i
同时访问该变量。Java 将变量缓存在 CPU 缓存中。由于每个线程都可以在不同的内核中执行,因此每个线程中的值i
可能不同。即使您正在执行i++
底层代码也是i = i + 1
. 您正在读取变量,然后正在写入,因此在这种情况下多个线程也可能具有不同的值。
这个问题的解决方案是像你的情况一样使用Atomic
变量。AtomicInteger
这些原子变量是线程安全的,并且像在事务中一样读写,在更新变量时阻塞对线程的访问。
在您的情况下,int
您可以声明而不是声明
private volatile AtomicInteger atomicInteger = new AtomicInteger(1);
而不是使用i++
你可以使用atomicInteger.addAndGet(1)
TA贡献1890条经验 获得超9个赞
您刚刚发现的是竞争条件线程可以counter
不受任何限制地访问对象。即使i
是 int,也不能保证,例如,当“t2”读取该值时,“t1”完成了该值的递增。尝试运行该程序几次,输出可能会有所不同。
要解决你的问题,你需要实现一种锁,或者使用关键字synchronized
。一般来说,在实现使用多线程的应用程序时,您需要识别关键部分并确保避免竞争条件。
编辑:我不敢相信我写的protected
不是synchronized
,不知道这是怎么发生的。就在今天偶然发现了这个,编辑所以没有其他人被误导。
添加回答
举报