java AtomicInteger decrementAndGet不能如期工作。

AtomicInteger的decrementAndGet在多线程操作中似乎不能正常工作。虽然incrementAndGet可以正常工作,但在多线程操作中,AtomicInteger的decrementAndGet似乎不能正常工作。我从Executors和10个线程中运行这个程序。每个线程在循环中减去5次值,所以10个线程会将其值减为-50。但是我没有得到-50的值。同样的事情,如果我调用incrementAndGet,那么它确实增加到50。

public class RunCountAtomic {

public static  AtomicInteger aValue = new AtomicInteger(0);

public void runLoop() {
    for(int i=0;i<5;i++) {
//      aValue.getAndIncrement();
            doSomeWork();
            aValue.decrementAndGet();
        }
    System.out.println(" Expected value is - (5 x 10 thread = -50) , actual is: " +aValue.get());
    }
    // Some dummy work!     
    public void doSomeWork() { try {Thread.sleep(100);  } catch (InterruptedException e) {}  } 
}

结果表明,增量和获取确实是正确的,但是减量和获取就不正确了!你们有人也有这样的观察吗?你们有人也有这样的观察吗?或者我可能在代码中遗漏了什么。

下面是调用上述类的方法的测试代码。

根据回复意见更新]更新我的TestMainRL代码,使用执行器服务来简化。

public static void main(String[] args) {
    int noOfThreads         = 10;
    ExecutorService e = Executors.newFixedThreadPool(noOfThreads); 

    List<Future<Boolean>>   fblist = new ArrayList<Future<Boolean>>(noOfThreads);  

    System.out.println("Starting");     
    // Run the async task in multiple threads.
    for (int i = 0; i<noOfThreads; i++) {
        TestMainRL t = new TestMainRL();
        fblist.add(t.asyncTask ( e));
    }

    //Just to wait all thread to complete.
    //try {for(Future<Boolean> fb: fblist) {fb.get();}} catch (Exception e1) {e1.printStackTrace();}
    e.shutdown();
    System.out.println("Completed!");

}

另外,下面是显示最后一个线程不是-50的结果。

Starting
Completed!
Thread[pool-1-thread-1,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -42
Thread[pool-1-thread-2,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -42
Thread[pool-1-thread-6,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -46
Thread[pool-1-thread-7,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -50
Thread[pool-1-thread-5,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -49
Thread[pool-1-thread-3,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -49
Thread[pool-1-thread-4,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -49
Thread[pool-1-thread-10,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -46
Thread[pool-1-thread-8,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -46
Thread[pool-1-thread-9,5,main] Expected value is - (5 x 10 thread = -50) , actual is: -46

在得到@acm的回复后,我意识到要实现目标,只有一个线程应该打印-50,而且用下面的代码就可以实现,下面的RunCountAtomic代码不使用ReentrantLock。

public class RunCountAtomic {
    public static  AtomicInteger aValue = new AtomicInteger(0);
    public void runLoop() {
        int x = 0;
        for(int i=0;i<5;i++) {
           doSomeWork();
           x = aValue.decrementAndGet(); // decrement and store it in local x.
       }
       // Here at the time of this query post, I was printing aValue.get() instead of using stored x,
       // which will ensure that only one thread will have -50.
       System.out.println(Thread.currentThread()+" Value in thread " +x);
    }
    public void doSomeWork() { 
        try {
             Thread.sleep(100);
        } catch (InterruptedException e) {} 
    } // Some dummy work!
}

得到-50的线程是最后一个线程,它进行了最后一次递减. 但是在做system.out的时候,它可能没有在最后打印。

解决方案:

你到底在期待什么?所有线程返回 -50,最后一条线返回 -50? 在你提供的代码中,这些都不能保证。

AtomicInteger 是按照预期进行的。你所保证的是至少有一个线程会返回 -50,而这一切都在发生吧?

关键点是 System.out.println -> PrintStream#println:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

所以你所有的线程都在叫这个,但是这种锁是不公平的。所以,尽管最后一个到达这个锁的线程是那个有值的线程 -50 它可能不是第一个被印出来的,所以你有保证的是 -50 是打印的,但在某些时候不是最后。

要测试解释什么,你可以使用 ReentrantLock 的公平性改为true,意味着最老的等待线程将首先被唤醒。将 RunCountAtomic 如下。

public class RunCountAtomic {
    private static final ReentrantLock lock = new ReentrantLock(true);
    public static AtomicInteger aValue = new AtomicInteger(0);

    public void runLoop() {
        for (int i = 0; i < 5; i++) {
            doSomeWork();
            aValue.decrementAndGet();
        }
        printlnValue(aValue.get());
    }

    private void printlnValue(int value) {
        lock.lock();
        try {
            System.out.println(" Expected value is - (5 x 10 thread = -50) , actual is: " + value);
        } finally {
            lock.unlock();
        }
    }

    public void doSomeWork() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {}
    }
}

现在保证你有 -50 最后一次印刷。

给TA打赏
共{{data.count}}人
人已打赏
未分类

如何解决StatusCode.415, ReasonPhrase: 'Unsupported Media Type'? 415,ReasonPhrase: 'Unsupported Media Type'?

2022-9-8 23:39:38

未分类

如何在C语言中使用sscanf解析json?

2022-9-8 23:39:40

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索