Java多线程:意外的结果

前端之家收集整理的这篇文章主要介绍了Java多线程:意外的结果前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在开发一个企业应用程序.我在多线程环境中运行应用程序时遇到了一些问题.我正在编写一个程序,其中有一个变量,其值以非常快的速率更新(递增)(例如10000次更新/ persecond).循环运行一定的迭代,变量的值递增并存储在HashMap中.一旦循环终止并且值打印HashMap中的变量.我得到了变量的意外价值.

这是演示程序(请阅读评论以便更好地理解):

class test implements Runnable {

    static ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
    static AtomicInteger value_to_be_incremented_stored = new AtomicInteger(0); // variable whose value to be updated
    static AtomicInteger i = new AtomicInteger(0);  // this runs the loop

    @Override
    public void run() {

        for (i.set(0); i.get() < 100000; i.incrementAndGet()) {
            /*
                This loop should run 100000 times and when loop terminates according to me value of variable 
                "value_to_be_incremented_stored" should be 100000 as its value is incremented 
                100000 times the loop also runs 100000 times. 
            */
            System.out.println("Thread > " + Thread.currentThread() + "  " + value_to_be_incremented_stored.incrementAndGet());
            map.put("TC",value_to_be_incremented_stored.intValue());
        }

        System.out.println("Output by Thread  " + Thread.currentThread() + "     " + map.toString());
    }

    public static void main(String[] args) {

        test t1 = new test();
        Thread thread1 = new Thread(t1);
        thread1.setName("Thread 1");

        Thread thread2 = new Thread(t1);
        thread2.setName("Thread 2");

        Thread thread3 = new Thread(t1);
        thread3.setName("Thread 3");

        Thread thread4 = new Thread(t1);
        thread4.setName("Thread 4");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }
}

输出(变化):

问题 :
我正在运行循环100000次(i.get()< 100000)然后变量value_to_be_incremented_stored的值如何变得超过100000.

解决方法

我发现了三个缺陷.在比较循环计数器的点与增加循环计数器的位置之间的for循环中存在竞争条件.您应该在一个步骤中执行此操作以获取原子操作:
for ( ; i.incrementAndGet() < 100000;  ) {

另一个是你的计数器增量和放在地图之间也存在竞争条件.即使你将它们串联递增,任何线程都可以在内部具有不同的值(它位于循环中的不同点),并且它可以将先前的值放在全局映射中.这里需要原子性以确保增加的值是您在循环中放置的值.

synchronized( lock ) { 
    value_to_be_incremented_stored.incrementAndGet();
    map.put("TC",value_to_be_incremented_stored.intValue());
}

最后由于某种原因<比较产生的值为99999.我不得不使用< =来修复它. (正如我们在评论中所讨论的那样,在每个for循环开始时设置i.set(0)并不是很明显的原因.我猜想有四个缺陷.)

class ThreadTestX implements Runnable {

    static ConcurrentHashMap<String,Integer> map = new ConcurrentHashMap<>();
    static AtomicInteger value_to_be_incremented_stored = new AtomicInteger(0); // variable whose value to be updated
    static AtomicInteger i = new AtomicInteger(0);  // this runs the loop
    static final Object lock = new Object();

    @Override
    public void run() {

        for ( ; i.incrementAndGet() <= 100000;  ) {
            /*
                This loop should run 100000 times and when loop terminates according to me value of variable 
                "value_to_be_incremented_stored" should be 100000 as its value is incremented 
                100000 times the loop also runs 100000 times. 
            */
            synchronized( lock ) {
                value_to_be_incremented_stored.incrementAndGet();
    //            System.out.println("Thread > " + Thread.currentThread() + 
    //                 "  " + value_to_be_incremented_stored.get());
                map.put("TC",value_to_be_incremented_stored.intValue());
            }
        }

        System.out.println("Output by Thread  " + Thread.currentThread() 
                + "     " + map.toString());
    }

    public static void main(String[] args) {

        ThreadTestX t1 = new ThreadTestX();
        Thread thread1 = new Thread(t1);
        thread1.setName("Thread 1");

        Thread thread2 = new Thread(t1);
        thread2.setName("Thread 2");

        Thread thread3 = new Thread(t1);
        thread3.setName("Thread 3");

        Thread thread4 = new Thread(t1);
        thread4.setName("Thread 4");

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }
}

输出

run:
Output by Thread  Thread[Thread 4,5,main]     {TC=100000}
Output by Thread  Thread[Thread 3,main]     {TC=100000}
Output by Thread  Thread[Thread 1,main]     {TC=100000}
Output by Thread  Thread[Thread 2,main]     {TC=100000}
BUILD SUCCESSFUL (total time: 0 seconds)

事后判断:尽管标记正确,但我不确定我是否正确.这里的问题是你试图让三件事情保持同步:循环计数器i,要增加的值和地图.允许在同步块之外执行这些中的任何一个可以邀请它们处于意外状态.我认为以下可能更安全:

@Override
public void run() {

    for ( ;;  ) {
        synchronized( lock ) {
            if( i.incrementAndGet() <= 100000 ) {
                value_to_be_incremented_stored.incrementAndGet();
                map.put("TC",value_to_be_incremented_stored.intValue());
            }
            else
                break;
        }
    }
    System.out.println("Output by Thread  " + Thread.currentThread() 
            + "     " + map.toString());
}

这消除了将变量声明为AtomicInteger的需要,但是我没有看到如何确保它们的值在该循环执行时不会改变(由于某些其他线程).

猜你在找的Java相关文章