程序并不总是终止?

时间:2018-04-23 14:41:19

标签: java multithreading

我想测试我的一个项目的多线程,试图在出现问题的情况下开发解决方案。

所以我做了这个小测试:

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);
            }

    }

识别TestClass

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次)。

任何修复?

2 个答案:

答案 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++