【问题标题】:Performance Difference of AtomicInteger vs IntegerAtomicInteger 与 Integer 的性能差异
【发布时间】:2012-01-16 10:25:06
【问题描述】:

AtomicIntegerInteger 之间有性能差异吗?

【问题讨论】:

    标签: java concurrency atomicinteger


    【解决方案1】:

    这两种类型的选择不应取决于性能。 AtomicInteger 的主要选择是如果你想通过对整数的操作来实现线程安全。

    但是,性能差异可能很大程度上取决于所选的操作系统,因为原子操作的详细实现取决于操作系统。

    【讨论】:

    • +1。 AtomicInteger 还提供了一个廉价的可变整数,即使在非并发用例中也是如此。
    • -1:AtomicInteger 的存在主要是出于性能原因(实现无锁数据结构)。如果广告安全是唯一的问题,您可以简单地使用同步。
    • @MichaelBorgwardt:仅仅因为同步就可以实现线程安全并不意味着 AtomicInteger 不提供线程安全的整数操作。答案的重点是 Integer 和 AtomicInteger 的原始性能并不是指导选择其中之一的因素。指导选择的是预期用途:我们需要一个可变的线程安全整数,还是我们只是想将一个整数转换成一个不可变的对象?
    • @JB Nizet:答案中没有提到可变性。除此之外,当出现一个选择之一时,如何表现不能指导选择?
    • @MichaelBorgwardt:OP 的问题甚至没有提到多线程。他只是询问 Integer 和 AtomicInteger 之间是否存在性能差异。 AtomicInteger 尚未创建为具有更快的 Integer。创建它是为了提供线程安全、无锁、可变的整数。如果您不需要可变性并且不在多线程环境中执行,那么两者的性能都无关紧要。您选择最合适的类型:整数。只有在需要可变性(和线程安全)时才应考虑 AtomicInteger。
    【解决方案2】:

    AtomicInteger 允许使用特殊硬件指令以无锁方式执行一些(不是全部!)操作,否则这些操作将需要同步。这对性能的影响有点复杂:

    • 首先,它是一种微优化,仅当此特定操作位于应用程序的关键路径上时才有意义。
    • 特殊硬件指令可能在非主流平台上不可用,这种情况下AtomicInteger可能会使用同步来实现。
    • JVM 通常可以在没有争用时优化掉锁定开销(例如,单线程应用程序)。在这种情况下,可能没有区别。
    • 如果存在低到中度的锁争用(即多个线程,但它们主要执行其他操作而不只是访问该整数),则无锁算法的性能优于同步。
    • 如果存在非常严重的锁争用(即大量线程花费大量时间试图访问该整数),同步可能会执行得更好,因为无锁算法基于在由于以下原因而失败时不断重试操作碰撞。

    【讨论】:

    • 正是我一直在寻找的事实类型。谢谢。
    【解决方案3】:

    好吧,如果您在多线程环境中使用它,例如,计数器,那么你必须以synchronize 访问Integer

    public final class Counter {
      private long value = 0;
      public synchronized long getValue() {
        return value;
      }
    
      public synchronized long increment() {
        return ++value;
      }
    }
    

    虽然您可以在不同步的情况下使用 AtomicInteger 获得更好的性能

    public class NonblockingCounter {
        private AtomicInteger value;
    
        public int getValue() {
            return value.get();
        }
    
        public int increment() {
            return value.incrementAndGet();
        }
    }
    

    推荐阅读http://cephas.net/blog/2006/09/06/atomicinteger/

    编辑使用 incrementAndGet

    【讨论】:

    • 为什么不使用 incrementAndGet?
    • 你的意思是getAndIncrement?好吧,好问题先生,我想我太快太懒了。
    • 不,我的意思是 incrementAndGet。 getAndIncrement 相当于返回 i++。但是你的第一个 sn-p 返回 ++i;
    • 嗯...是啊,也许我现在应该只专注于我的工作,谢谢。
    • 另外,IBMWorks 上的 Brian Goetz(新链接)ibm.com/developerworks/library/j-jtp11234/index.html
    【解决方案4】:

    今天看到这个帖子,但想分享我的结果(请不要在代码上使用 cmets,因为我必须手动输入以下类,因为我运行这个的系统没有连接到互联网 :)

    下面代码的输出如下:

    原子结果:Elapsed = 25257 ms,ExpectedValue = 50000,FinalValue = 50000,true PrImItIvE 结果:Elapsed = 25257 ms,ExpectedValue = 50000,FinalValue = 48991,false

    对于我在特定应用程序中的使用,我选择在监控类中使用原子值作为状态编号。如果其他人想查看一些硬结果,我选择发布此信息。

    祝你有美好的一天!

    类:

    我创建了一个主类,其中包含一个原始 long 和一个原子 long 以及访问器增量方法、一个 IncrementAtomicRunnable 和一个 IncrementPrimitiveRunnable。

    LongOverhead:

    public class LongOverhead{
      AtomicLong atomicLong;
      long primitiveLong;
    
      public LongOverhead(){
        atomicLong = new AtomicLong(0l);
        primitiveLong = 0l;
      }
    
      public void incrAtomicLong(){
        atomicLong.getAndAdd(1l);
      }
    
      public long getAtomicLong(){
        return atomicLong.get();
      }
    
      public void incrPrimitiveLong(){
        primitiveLong++;
      }
    
      public long getPrimitiveLong(){
        return primitiveLong;
      }
    
      public static void main(String [] args){
        String template = "%s Results:  Elapsed = %d ms, ExpectedValue = %d, FinalValue = %d, %b";
    
        int loopTotal = 1000;
        int waitMilliseconds = 25;
        int totalThreads = 50;
        int expectedValue = loopTotal * totalThreads;
        int whileSleep = 250;
    
        LongOverhead atomic = new LongOverhead();
        LongOverhead primitive = new LongOverhead();
    
        List<Thread> atomicThreads = new ArrayList<>();
        List<Thread> primitiveThreads = new ArrayList<>();
    
        for(int x=0;x<totalThreads;x++){
          Thread a = new Thread(new IncrementalAtomicRunnable(atomic, loopTotal, waitMilliseconds), "AtomicIncr" + x);
          atomicThreads.add(a);
    
          Thread p = new Thread(new IncrementalPrimitiveRunnable(primitive, loopTotal, waitMilliseconds), "PrimitiveIncr" + x);
          primitiveThreads.add(p);
        }
    
        boolean cont = true;
        long atomicStart = System.currentTimeMillis();
        for(Thread t:  atomicThreads){
          t.start();
        }
    
        while(cont){
          try{
            Thread.sleep(whileSleep);
          }catch(InterruptedException e){
             e.printStackTrace();
          }
    
          boolean foundAlive = false;
          for(Thread t: atomicThreads){
            foundAlive = (State.TERMINATED != t.getState());
            if(foundAlive){
              break;
            }
          }
    
          cont = foundAlive;
    
        }
    
        long atomicFinish = System.currentTimeMillis();
        long atomicElapsed = atomicFinish - atomicStart;
        long atomicFinal = atomic.getAtomicLong();
    
        cont = true;
        long primitiveStart = System.currentTimeMillis();
        for(Thread t:  primitiveThreads){
          t.start();
        }
    
        while(cont){
          try{
            Thread.sleep(whileSleep);
          }catch(InterruptedException e){
             e.printStackTrace();
          }
    
          boolean foundAlive = false;
          for(Thread t: primitiveThreads){
            foundAlive = (State.TERMINATED != t.getState());
            if(foundAlive){
              break;
            }
           }
    
           cont = foundAlive;
        long primitiveFinish = System.currentTimeMillis();
        long primitiveElapsed = primitiveFinish - primitiveStart;
        long primitiveFinal = primitive.getPrimitiveLong();
    
        System.out.println(String.format(template, "ATOMIC", atomicElapsed, expectedValue, atomicFinal, (expectedValue==atomicFinal)));
        System.out.println(String.format(template, "PrImItIvE", primitiveElapsed, expectedValue, primitiveFinal, (expectedValue==primitiveFinal)));
      }
    

    IncrementAtomicRunnable:

    public class IncrementAtomicRunnable implements Runnable{
      protected LongOverhead oh;
      protected int loopTotal;
      protected int waitMilliseconds;
      protected String currentThreadName;
    
      public IncrementAtomicRunnable(LongOverhead oh, int loopTotal, int waitMilliseconds){
        this.oh = oh;
        this.loopTotal = loopTotal;
        this.waitMilliseconds = waitMilliseconds;
      }
    
      @Override
      public void run(){
        currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " for ATOMIC is starting.....");
        for(int x=0;x<loopTotal;x++){
          oh.incrAtomicLong();
          try{
            Thread.sleep(waitMilliseconds);
          }catch(InterruptedException e){
            System.out.println("InterruptedThread[" + currentThreadName + "], eating exception @@@@@");
          }
        }
    
        System.out.println("....." + currentThreadName + " for ATOMIC is finished.");
      }
    }
    

    最后 IncrementPrimitiveRunnable:

    public class IncrementPrimitiveRunnable extends IncrementAtomicRunnable{
      public IncrmentPrimitiveRunnable(LongOverhead oh, int loopTotal, int waitMilliseconds){
        super(oh, loopTotal, waitMilliseconds);
      }
    
      @Override
      public void run(){
        super.currentThreadName = Thread.currentThread().getName();
        System.out.println(currentThreadName + " for PRIMITIVE is starting.....");
        for(int x=0;x<loopTotal;x++){
          oh.incrPrimitiveLong();
          try{
            Thread.sleep(waitMilliseconds);
          }catch(InterruptedException e){
            System.out.println("InterruptedThread[" + currentThreadName + "], eating exception @@@@@");
          }
        }
    
        System.out.println("....." + currentThreadName + " for PRIMITIVE is finished.");
      }
    }
    

    【讨论】:

      【解决方案5】:

      除了非常小的同步开销,没有。

      【讨论】:

      • 不涉及同步
      • 它使用 CAS 指令。你不会在课堂上的任何地方找到“同步”这个词。原子类的重点是避免同步。
      猜你喜欢
      • 2013-04-03
      • 2013-04-14
      • 2011-12-17
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 2019-11-15
      • 2020-10-15
      相关资源
      最近更新 更多