精华内容
下载资源
问答
  • Java并发计数器探秘
    2020-05-04 00:10:49
    更多相关内容
  • 主要给大家介绍了关于Java并发计数器的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • java 并发计数器 我只是买了新玩具,而Java 8有很多 。 这次我想谈谈我的最爱之一-并发加法器。 这是一组新的类,用于管理由多个线程编写和读取的计数器。 新的API有望显着提高性能,同时仍使事情简单明了。 ...

    java 并发计数器

    博客羊

    我只是买了新玩具,而Java 8有很多 。 这次我想谈谈我的最爱之一-并发加法器。 这是一组新的类,用于管理由多个线程编写和读取的计数器。 新的API有望显着提高性能,同时仍使事情简单明了。

    自从多核体系结构问世以来人们一直在管理并发计数器,让我们看一看迄今为止Java提供的一些选项以及与新API相比它们的性能。

    计数器 –这种方法意味着您正在多个线程之间的常规对象或静态字段中进行写入/读取操作。 不幸的是,这有两个原因。 首先是在Java中,A + = B操作不是原子操作。 如果打开输出字节码,将至少看到四条指令-一个用于将堆中的字段值加载到线程堆栈中,第二个用于加载增量,第三个用于添加它们,第四个用于设置结果进入领域。

    如果在同一个内存位置同时有多个线程同时执行此操作,则极有可能错过写操作,因为一个线程可以覆盖另一个线程的值(又称“读取-修改-写入”) 。 与此相关的还有另一个讨厌的角度,那就是价值的波动性。 下面的更多内容。

    这是一个菜鸟错误,而且很难调试。 如果您确实遇到了在您的应用程序中执行此操作的任何人,我想请您帮个忙。 在数据库中搜索“ Tal Weiss”。 如果您在那里看到我–删除我的记录。 我会更安全的。

    同步 -这是最基本的并发习惯用法,它在读取或写入值时会阻塞所有其他线程。 当它起作用时,这是将代码转换为DMV行的可靠方法。

    RWLock –基本Java锁的这种稍微复杂的版本,使您可以区分更改了值并需要阻止其他线程的线程与仅读取且不需要关键部分的线程。 尽管这可能更有效(假设编写器的数量很低),但是这是一个相当不错的方法,因为在获取写锁时,您将阻止所有其他线程的执行。

    易失性 -这个相当容易被误解的关键字实际上指示JIT编译器取消优化运行时机器代码,以便其他线程可以立即看到对该字段的任何修改。

    这使一些JIT编译器最喜欢的优化工作失去了分配分配到内存的顺序。 再说一次 你听到了 JIT编译器可以更改对字段进行分配的顺序。 这种不可思议的小策略(也称为before-before )使它可以最小化程序访问全局堆所需的次数,同时仍确保您的代码不受其影响。 偷偷摸摸的…

    那么,什么时候应该使用易失性计数器? 如果只有一个线程在更新一个值,而有多个线程在使用它,那么这是一个非常好的策略–根本没有争用。

    那么为什么不总是问它呢? 因为当一个以上的线程正在更新该字段时,这不能很好地工作。 由于A + = B不是原子的,因此存在覆盖其他人的写入的风险。 在Java 8之前,您需要使用AtomicInteger。

    AtomicInteger-这组类使用CAS(比较和交换)处理器指令来更新计数器的值。 听起来不错,不是吗? 好吧,是的,不是。 这很有效,因为它利用直接的机器代码指令来设置该值,而对其他线程的执行影响最小。 缺点是,如果由于与另一个线程的争用而无法设置该值,则必须重试。 在竞争激烈的情况下,这可能会变成自旋锁,其中线程必须不断尝试并在无限循环中设置该值,直到成功为止。 这不是我们想要的。 输入带有LongAdders的Java 8。

    Java 8 Adders –这是一个非常酷的新API,我永不过时! 从使用角度来看,它与AtomicInteger非常相似。 只需创建一个LongAdder并使用intValue()和add()即可获取/设置值。 魔术发生在幕后。

    此类的作用是,当直接CAS由于争用而失败时,它将增量存储在为该线程分配的内部单元对象中。 然后在调用intValue()时将待处理单元格的值加到总和上。 这减少了返回和CAS或阻止其他线程的需要。 很聪明的东西!

    这么好说吧-让我们看看这只小狗在行动。 我们建立了以下基准:将计数器重置为零,并开始使用多个线程读取和递增计数器。 当计数器达到10 ^ 8时停止。 我们在4核i7处理器上运行了基准测试。

    我们使用总共十个线程来运行基准测试-五个用于写作,五个用于阅读,因此我们在这里势必会引起严重的争论:

    • 请注意,肮脏和易变的风险值都将覆盖。

    table_april-16_02-2

    底线

    • 并发加法器洁净室的性能比原子整数提高60-100%
    • 除了锁定时,添加线程没有什么区别。
    • 请注意,使用同步锁或RW锁会给您带来巨大的性能损失-慢一个数量级!

    如果您已经有机会在代码中使用这些类,那么我很乐意听到。

    翻译自: https://www.javacodegeeks.com/2014/04/java-8-longadders-the-right-way-to-manage-concurrent-counters.html

    java 并发计数器

    展开全文
  • Java并发 - 计数器

    2021-08-21 16:46:54
    可理解为减法计数器 public void testCountDownLatch() throws InterruptedException { // 减法计数器初始值 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new ...

    CountDownLatch

    可理解为减法计数器

    public void testCountDownLatch() throws InterruptedException {
        // 减法计数器初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "---> 执行完毕");
                // 数量减1
                countDownLatch.countDown();
            },"线程"+i).start();
        }
        // 等待计数器归零,再继续向下执行
        countDownLatch.await();
        System.out.println("所有线程执行完毕");
    }
    

    CyclicBarrier

    可理解为加法计数器

    public void testCyclicBarrier() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6, () -> {
            System.out.println("所有线程执行完毕");
        });
    
        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "---->" + temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
    

    JDK8新增了以下更新器和计数器的API:

    更新器:DoubleAccumulator、LongAccumulator

    计数器:DoubleAdder、LongAdder

    计数器增强版,高并发下性能更好。经过以下三中方式对比,发现LongAdder性能是最好的。

    public class LongAdderDemo {
        private long count = 0;
        public void testSync() throws InterruptedException {
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    long starttime = System.currentTimeMillis();
                    while (System.currentTimeMillis() - starttime < 2000) {
                        synchronized (this) {
                            ++count;
                        }
                    }
                    long endtime = System.currentTimeMillis();
                    System.out.println("SyncThread spend:" + (endtime - starttime) + "ms,count=" + count);
                }).start();
            }
        }
    
        private AtomicLong count2 = new AtomicLong(0L);
        public void testAtomic() {
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    long starttime = System.currentTimeMillis();
                    while (System.currentTimeMillis() - starttime < 2000) {
                        count2.incrementAndGet();
                    }
                    long endTime = System.currentTimeMillis();
                    System.out.println("AtomicThread spend:" + (endTime - starttime) + "ms,count=" + count2.get());
                }).start();
            }
        }
    
        private LongAdder count3 = new LongAdder();
        public void testLongAdder() {
            for (int i = 0; i < 3; i++) {
                new Thread(() -> {
                    long starttime = System.currentTimeMillis();
                    while (System.currentTimeMillis() - starttime < 2000) {
                        count3.increment();
                    }
                    long endTime = System.currentTimeMillis();
                    System.out.println("LongAdderThread spend:" + (endTime - starttime) + "ms,count=" + count3.sum());
                }).start();
            }
        }
    
        public static void main(String[] args) throws Exception{
            LongAdderDemo demo = new LongAdderDemo();
            demo.testSync();
            demo.testAtomic();
            demo.testLongAdder();
        }
    }
    

    运行结果:

    SyncThread spend:2000ms,count=27297961
    SyncThread spend:2000ms,count=27297961
    SyncThread spend:2000ms,count=27297961
    LongAdderThread spend:2000ms,count=233398030
    LongAdderThread spend:2000ms,count=233398030
    AtomicThread spend:2000ms,count=96741082
    AtomicThread spend:2000ms,count=96741082
    LongAdderThread spend:2000ms,count=233398030
    AtomicThread spend:2000ms,count=96741082
    

    LongAdder适用于频繁更新但不会频繁读取。汇总统计信息时使用分成多个操作单元,不同线程更新不同的单元,只有需要汇总的时候才计算所有单元的操作。

    一般应用于统计次数,例如统计接口的调用次数。

    LonAdder 的increment()和sum()方法:

    public void increment() {
        add(1L);
    }
    public void add(long x) {
        // 使用多个内存单元,线程分开累加,保证线程安全又能提高效率
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }
    
    public long sum() {
        Cell[] as = cells; Cell a;
        long sum = base;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }
    

    sum方法中在累加每个单元的运算结果。

    LongAdder在JDK8后新增了一个增强类LongAccumulator:

    public void testLongAdderAccumulator() {
        LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {
            @Override
            public long applyAsLong(long left, long right) {
                // 返回最大值,自定义返回值
                return left > right ? left : right;
            }
        }, 0);
    
        for (int i = 0; i < 100; i++) {
            int finalI = i;
            new Thread(() -> {
                accumulator.accumulate(finalI);
            }).start();
        }
        // 获取指定返回的值,如最大值、中间值 、最小值等
        System.out.println(accumulator.get());
    }
    
    展开全文
  • 多核时代来临了之后,大家都开始使用并发计数器,我们先来看一下Java迄今为止提供了哪些实现方式,它们的性能和这个新的API相比,又有什么不同。脏计数器——选择这种方式意味着多线程直接并发读写一个普通...

    我喜欢新鲜玩意儿,而Java 8里面就有不少。这回我准备介绍一下我的一个最爱——并发计数器。这是一组新的类,用于维护多个线程并发读写的计数器。新的API带来了显著的性能提升,同时还保证了接口的简单易用。

    多核时代来临了之后,大家都开始使用并发计数器,我们先来看一下Java迄今为止提供了哪些实现方式,它们的性能和这个新的API相比,又有什么不同。

    脏计数器——选择这种方式意味着多线程直接并发读写一个普通对象或者静态字段。不幸的是,这么做是行不通的。有两个原因,一个是在Java里, A+= B操作不是原子的。如果你打开编译后的字节码看一下,你会发现至少有四条指令——第一条是从堆里将字段值加载到线程栈里,第二条是加载要增加的值,第三条指令将它们进行相加,第四条则将结果写回到字段中。

    如果多个线程同时在同一个内存位置进行这个操作,你的一个写操作很有可能就丢掉了,因为另一个线程可能会覆盖了它的值。还有一个很恶心的事就是这个值的可见性。下面还会详细介绍到。

    新手非常容易犯这样的错误,而这样的问题却很难发现。如果你发现团队中有人这么做,最好能帮我个小忙。在你的数据库里面搜一下我的名字“Tal Weiss"。如果你发现我在里面——赶紧把我的记录删掉。这样我会感觉舒服点。

    synchronzied——这是最基础的同步操作了,只要你在读写值,它就会阻塞住其它的所有线程。这种方式的确行得通,不过肯定的是,你的程序运行起来会像DMV排的长队那样。

    读写锁(RWLock)——这个和基础的Java锁相比就巧妙了些,它可以让你区分出那些要修改值因此需要阻塞别人的进程以及那些只是读取值不需要进入临界区的。虽然这个方法有的时候很高效(比如写线程的数量比较少的话),但还是相当无语,因为当你获取写锁的时候还是会阻塞住其它线程的执行。

    volatile——这个经常会被误用的关键字会让JIT编译停止在运行时进行机器码的优化工作,因此字段一旦有更新别的线程马上就能看到。

    它会使得JIT编译器经常玩的一些把戏比如说调整赋值语句的顺序这些无法进行。JIT编译器有可能会改变字段的赋值顺序。什么,你再说一遍?是的,你听的没错。这个神秘的小把戏使得它可以减小程序访问全局堆的次数,同时它还能保证不会影响到你的程序的执行。这真是有点偷偷摸摸的感觉。

    那什么时候应该使用volatile计数器?如果你只有一个线程在更新一个值,而多个线程在读的话,这是个很合适的场景。因为完全没有竞争。

    你可能会问为什么都使用它就完了?因为如果有多个线程在更新的话就会有问题了。由于A+=B不是一个原子操作,这么做的话可能会覆盖掉别人写的话。在Java 8以前,这种情况你就只能用AtomicInteger了。

    AtomicInteger——这组类使用了处理器的CAS (compare-and-swap)指令来更新计数器的值。听起来不错吧?一半一半吧。由于它直接使用机器指令来设置值,因此对其它线程的影响最小。不好的一面是如果它和别的线程有竞争赋值失败了,它会继续重试。在高并发的条件 下,这就成了一个自旋锁,线程会在一个无限的循环内不断的尝试赋值,直到成功为止。我们可不太想看到这种局面。Java 8来了,还带来了LongAdders。

    Java 8 Adders——这是个非常棒的新的API,我对它的仰慕有如滔滔江水连绵不绝。从使用者的角度来说,它很像AtomicInteger。只需要创建一个LongAdder对象,然后使用intValue()以及add()方法来获取和设置它的值。而奇迹就发生在这一切的背后。

    如果由于竞争这个类的CAS操作失败了的话,它会要添加的值存到一个线程本地的内部的cell对象里。当intValue()方法调用 的时候,它把这些cell的值加到总和里。这样就减少了CAS重试或者阻塞别的线程的情况。真不错的想法。

    说的也差不多了。我们来看看它的真本事。我们做了如下的一个基准测试:把一个计数器设置为0,然后多个线程开始读取并进行自增。当计数器到达10^8的时候停止。我们在一个4核的i7处理器上运行这个测试。

    我用了10个线程来运行这个基准测试——读写分别使用5个线程来进行,这样的话会出现严重的竞争条件:

    注意:脏读和volatile都有可能产生脏值。

    table_april-16_02-2.jpg

    测试的代码在这里。

    结论

    并发的Adder类和AtomicInteger相比有60~100%的性能提升。

    增加线程不会对结果有太大影响,除非是使用锁的情况。

    注意到如果使用synchronized或者读写锁,性能会有很大的损耗——慢了一个数量级!

    如果你已经在代码里使用到它了——我会感到非常高兴。

    展开全文
  • 主要为大家详细介绍了Redis实现高并发计数器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • Java并发计数器多1

    2021-02-27 18:14:43
    在学习并发。尝试做一个计数器。 然后不知道为什么有时候计数会多1.有时候又不会。请问要怎样才能完成正确的计数。public class IncrementTest {static class Counter {private AtomicInteger count = new ...
  • 0x01,CountDownLatch介绍CountDownLatch是一个计数器,作为java并发编程中三个组件之一,这个组件的使用频率还是很多的。这里分享下自己画的java并发编程组...
  • 示例代码AtomicLong 实现的计数器Java 8 LongAdder更高效! 详情@Servicepublic class EnterpriseNameSuffixService implements InitializingBean {@Autowiredprivate EnterpriseNameSuffixRepository ...
  • java并发计数器问题

    2017-08-04 16:18:54
    java并发计数器问题
  • CountDownLatch是通过一个计数器来实现的,当我们在new 一个CountDownLatch对象的时候需要带入该计数器值,该值就表示了线程的数量。下面我们来深入了解一下吧
  • Java并发(三)——同步计数器

    千次阅读 2017-05-15 08:56:12
    1 CountDownLatch同步计数器1.1 类的概述  倒计数门闩。在完成一组正在其它线程中执行的操作之前,它允许一个或多个线程一直等待。在计数器到达0之前,await方法会一直阻塞;之后,会释放所有等待线程,await的...
  • 业务需求中经常有需要用到计数器的场景:譬如一个手机号一天限制发送5条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。使用Redis的Incr自增命令可以轻松实现以上需求。以一个接口一天限制调用...
  • 假设你是架构师,请用多线程相关基础知识实现单机 环境下,在3s内实现1亿的计数,并统计亿级计数的耗时。 要求:请运用11种方法实现亿级的并发计数。
  • www.jianshu.com/p/bcfe78fb9… 转载于:https://juejin.im/post/5cbe6a5ee51d456e5977b19f
  • 这是java并发系列第32篇文章。java环境:jdk1.8。本文主要内容4种方式实现计数器功能,对比其性能介绍LongAdder介绍LongAccumulator需求...
  • 业务需求中经常有需要用到计数器的场景:譬如一个手机号一天限制发送5条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。使用Redis的Incr自增命令可以轻松实现以上需求。以一个接口一天限制调用...
  • 并发计数器 并发计数器几乎是每个系统的一部分,用于收集数据,线程同步等。Java对基于堆的计数器有很好的支持。 当您需要可以在处理器之间共享的计数器时,可能会有用例。 如何建立进程间计数器 数据库 这是...
  • CountDownLatch属于java.util.concurrent包下; CountDownLatch类使一个线程等待其他线程各自执行完毕后再执行; 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,...
  • 一、说明利用redis操作的原子性,实现java 多线程并发的情况下实现计数器。我本机测试多个线程操作之后,结果会出现一定的延迟,但是最终数字是ok的应该是redis内部做了一个类似于队列的功能。需要注意的是,得使用...
  • 利用Redis实现高并发计数器

    万次阅读 热门讨论 2018-03-02 22:58:57
    业务需求中经常有需要用到计数器的场景:譬如一个手机号一天限制发送5条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。使用Redis的Incr自增命令可以轻松实现以上需求。以一个接口一天限制调用...
  • 文章目录1、业务需求背景2、代码实现2.1、RedisConfig.java2.2、RedisController.java2.3、redis_incr.lua3、测试效果4、总结 1、业务需求背景 一个手机号一天限制发送5条短信、一个接口一分钟限制多少请求、一个...
  • java并发编程三剑客

    2020-12-21 10:52:43
    目录java并发编程三剑客思维导图CountDownLatch用法构造器以及方法构造器主要方法使用方法CyclicBarrier用法构造器以及主要方法构造器主要方法使用方法Semaphore用法构造器和主要方法构造器主要方法使用方法三种辅助...
  • 需求是一分钟内接口的调用次数不超过1000次 次数放在redis缓存里。
  • Java并发

    2021-04-18 01:51:41
    Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而main函数所在的线程就是这个进程中的一个线程,也称主线程。何为线程?线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的...
  • 多核时代来临了之后,大家都开始使用并发计数器,我们先来看一下Java迄今为止提供了哪些实现方式,它们的性能和这个新的API相比,又有什么不同。 脏计数器——选择这种方式意味着多线程直接并发读写一个...
  • java并发工具类1、CountDownLatch :能够使一个或多个线程等待其他线程完成各自的工作后再执行2、CyclicBarrier : 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,...
  • Java线程安全计数器

    2021-03-29 00:37:10
    这篇博客主要记录下Java在多线程情况下,集中线程安全的计数器的一个实现。主要使用了三种不同的方式实现。 synchronized方式,排它锁、独占锁、非公平锁,悲观锁 Lock方式,公平锁,非公平锁,悲观锁 CAS自旋锁,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 78,643
精华内容 31,457
关键字:

java并发计数器

java 订阅