我想测试我的一个项目的多线程,试图在出现问题的情况下开发解决方案。
所以我做了这个小测试:
public class main
{
static int addToCounter;
static int addToErrorCounter;
public static void main(String[] args) throws InterruptedException
{
int threads = 10;
Executor exec = new Executor();
for (int i = 0; i < threads; i++)
{
double error = Math.random();
testClass aldo = new testClass();
Thread thread = aldo.getThread(300, error);
exec.execute(thread);
}
while (threads != (addToCounter + addToErrorCounter))
{
System.out.println("Not all threads finished, number of finished threads is: " + (addToCounter + addToErrorCounter));
Thread.sleep(50);
}
System.out.println("Number of Threads that finished correctly: " + addToCounter);
}
}
import test1.main;
public class testClass
{
public Thread getThread(long time, double error)
{
Thread thread = new Thread()
{
public void run()
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
if (error > 0.5)
{
main.addToErrorCounter++;
throw new java.lang.Error("HELLO");
}
System.out.println("I DID THIS!");
main.addToCounter++;
}
};
return thread;
}
}
(你必须修复导入,我也使用自定义类Executor
,尽管这只是ExecutorService
的包装器
奇怪的行为是,它有时会正常工作,有时它不会(总终止线程数为9,虽然我可以清楚地看到它打印出“我这么做!”而错误恰好是10次)。
任何修复?
答案 0 :(得分:0)
问题可能是一种竞争条件。 “++”运算符不是原子的。 想象一下以下场景。有两个线程同时。两者都希望增加一个数字并完成。 该数字的初始值为0。 线程0读取数字,现在知道它是0。 线程1读取数字,现在知道它是0。 线程0(谁知道它是0)现在将1写入内存。 线程1不知道该数字已经改变,并且仍然认为该数字为0,因此他还将1写入内存。
你需要像同步机制这样的东西,比如锁,或信号量或其他东西。
有关详细信息,请查看此信息:http://winterbe.com/posts/2015/04/30/java8-concurrency-tutorial-synchronized-locks-examples/
对于您的示例,您可以使用该链接中的“同步”示例。
向您的主类添加一个方法,使其增加addToCounter
,并添加到addToErrorCounter
,以便从错误计数器中删除效果:
synchronized AddToError(int e){
addToError += e;
}
synchronized IncCounter(){
addToCounter++;
}
在测试类中的线程中调用这些方法,而不是将它们递增为非同步。
答案 1 :(得分:0)
我的猜测是后缀运算符(main.addToCounter++
)不是原子的。这行代码可能等同于:
int temp = main.addToCounter;
main.addToCounter = temp + 1;
return temp;
有多个线程同时执行此操作,两个线程可以获得temp
的相同值(因为两者都执行上述伪代码中的第一行,然后执行第二行),因此计数器一旦所有线程完成,总数将太小。有关详细信息,请参阅Why is i++ not atomic?。
在这种情况下的快速解决方法是使addToCounter
成为AtomicInteger,然后使用addToCounter.incrementAndGet()
代替addToCounter++
。