java多线程下LongAdder、CountDownLatch、CyclicBarrier、Phaser 的用法

前言

一文读懂java多线程下常用常考的阻塞方法LongAdder、CountDownLatch、CyclicBarrier、Phaser
包含演示代码


LongAdder

在高并发下LongAdder是如何工作的?

LongAdder采用了分段锁的概念,在高并发下,如果是连续递增,这LongAdder会分成几块进行递增然后再相加,从而提高效率
在这里插入图片描述


synchronized VS Atomic Vs LongAdder

在高并发下 synchronized VS Atomic Vs LongAdder谁的效率更高?

这是不一定的,需要看具体的应用场景和数据量

synchronized 是通过加锁保证了线程同步,相对来说效率要低一些

Atomic 类中都是原子操作,可以称之为无锁优化,底层通过unsafe实现,不能完全保证线程安全性
unsafe 是java很底层的一个类,里面大多直接调用了native(本地)方法实现。
我们不能直接看到,unsafe可以直接操作指针,内存。之前版本还可以调用出来使用,现在应该是不可以使用了

LongAdder 内部采用了分段锁的概念,就是在高并发自增的情况下,可以分出来几块自增,然后进行相加,从而提高效率

下面代码模拟1000线程,没有线程操作100000次,
synchronized VS Atomic Vs LongAdder 那个效率更高

package 线程同步LongAdder;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

/**
 * @program: solution
 * @description: LongAdder内部类似与分段锁,如果发现多个相同(比如递增的操作)
 * 它会分几块递增然后相加操作。
 * 所以在并发量很高的情况下,效率高于synchronized和atomic
 *在一千个线程每个线程操作十万次的情况下,效率对比
 * synchronized VS Atomic Vs LongAdder
 * @author: Wang Hai Xin
 * @create: 2022-11-08 10:17
 **/
public class testLongAdder {

    static Long count1 = 0L;
    static AtomicLong count2 = new AtomicLong(0);
    static LongAdder count3 = new LongAdder();

    public static void main(String[] args) throws InterruptedException {

        Thread[] threads = new Thread[1000];

        /*synchronized*/
        Object lock = new Object();
        for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(()->{
                    for (int j = 0; j < 100000; j++)
                        synchronized (lock){
                            count1++;
                        }
                });
        }

        long start = System.currentTimeMillis();
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("syn: " + count1 + " time: " + (end - start));
/*Atomic*/
        start = System.currentTimeMillis();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
                for (int j = 0; j < 100000; j++) {
                    count2.incrementAndGet();
                }
            });
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        end = System.currentTimeMillis();
        System.out.println("Atomic: " + count2.get() + " time: " + (end - start));
/*LongAdder*/
        start = System.currentTimeMillis();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
                for (int i1 = 0; i1 < 100000; i1++) {
                    count3.increment();
                }
            });
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        end = System.currentTimeMillis();
        System.out.println("LongAdder: " + count3.longValue() + " time: " + (end - start));
    }

}

运行结果

syn: 100000000 time: 4198
Atomic: 100000000 time: 2322
LongAdder: 100000000 time: 259

注意并不是说LongAdder一定比其它两个效率高,只能是在特定条件下。

CountDownLatch

CountDownLatch 作用和jion()方法类似,像一个门栓,调用CountDownLatch.countDown()就会减一
当减少到0之后线程会继续运行。是一个阻塞的方法。

CountDownLatch 主要用在一个线程等待多个线程的情况下使用

代码如下


package ReentrantLockLongAdder;

import java.util.concurrent.CountDownLatch;

/**
 * @program: solution
 * @description: CountDownLatch 使用演示
 * @author: Wang Hai Xin
 * @create: 2022-11-10 15:29
 **/
public class CountDownT {

    public static void main(String[] args) {
        CountDownT countDownT = new CountDownT();
        countDownT.usingCountDowm();
        countDownT.usingJion();

    }

    public void usingCountDowm(){
        Thread[] threads = new Thread[100];
        CountDownLatch countDownLatch = new CountDownLatch(threads.length);

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
                int result = 0;
                for (int i1 = 0; i1 < 10000; i1++) {
                    result++;

                }
                countDownLatch.countDown();
            });
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("end latch");

    }

    public void usingJion(){
        Thread[] threads = new Thread[100];
        CountDownLatch countDownLatch = new CountDownLatch(threads.length);

        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
               int result = 0 ;
                for (int i1 = 0; i1 < 10000; i1++) {
                    result++;
                }
                countDownLatch.countDown();
            });
            
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }
        for (int i = 0; i < threads.length; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("end join");
    }

}

运行结果

end latch
end join

CyclicBarrier

CyclicBarrier 也是一个阻塞的方法,不过与countDownLatch不同,CyclicBarrier类似与一个起跑线。当数量够了之后,一起起跑。

CyclicBarrier 用在多个线程相互等待的场景

如下代码 ,我们在创建的时候规定,满20个线程,执行程序。不满20个 就会阻塞在这里

package ReentrantLockLongAdder;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @program: solution
 * @description: CycliBarrier 用法演示
 * @author: Wang Hai Xin
 * @create: 2022-11-10 15:48
 **/
public class CyclicBarrierT {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(20, new Runnable() {
            @Override
            public void run() {
                System.out.println("满20人 ,发车");
            }
        });

        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
    }
}

Phaser

phaser 类似与周期性的CyclicBarrier, 可以通过构造函数或者使用 phaser.bulkRegister(7);注册

现在我们模拟一个结婚的场景,必须所有人到场才能吃饭,所有人离席 新郎新娘才能抱抱

代码演示如下:

package ReentrantLockLongAdder;

import 线程同步volatile单例.T;

import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;

/**
 * @program: solution
 * @description: Phaser 演示代码,Phaser用于分段阻塞
 * @author: Wang Hai Xin
 * @create: 2022-11-11 09:49
 **/
public class PhaserT {


    static  PhaserDemo phaser = new PhaserDemo();

    public static void main(String[] args) {


        phaser.bulkRegister(7);

        for (int i = 0; i < 5; i++) {
            final  int name = i;
            new Thread(
                new Person("p"+i)
            ).start();
            
        }

        new Thread(
                new Person("新郎")
        ).start();

        new Thread(
                new Person("新娘")
        ).start();

    }

    static class PhaserDemo extends Phaser  {

        /*参数 phase 表示第几个周期,registeredParties表示这个周期注册的线程数*/
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {

            switch (phase){
                case 0 :{
                    System.out.println("所有人到场"+registeredParties);
                    return false;
                }
                case 1:{
                    System.out.println("所有人吃完"+registeredParties);
                    return false;
                }
                case 2 :{
                    System.out.println("所有人离场"+registeredParties);
                    return false;
                }
                case 3 : {
                    System.out.println("新郎新娘抱抱"+registeredParties);
                    return true;
                }
                default:{
                    return true;
                }
            }


        }
    }

    static class Person implements Runnable {
        public String name = null;

        public Person(String s) {
            name = s;
        }

        /*睡眠方法*/
        void milliSleep(){
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }


        public void arrive(){
            milliSleep();
           System.out.printf( "%s 到达现场! \n",name);
            System.out.println();
            /*注册Phaser*/
            phaser.arriveAndAwaitAdvance();
        }

        public void eat(){
            milliSleep();
            System.out.printf("%s 开始吃饭",name);
            System.out.println();
            phaser.arriveAndAwaitAdvance();
        }
        public void leave(){
            milliSleep();
            System.out.printf("%s 离开",name);
            System.out.println();
            phaser.arriveAndAwaitAdvance();
        }

        public void hug(){
            milliSleep();
            if ("新郎".equals(name) || "新娘".equals(name)) {
                System.out.printf("%s 洞房",name);
                System.out.println();
                phaser.arriveAndAwaitAdvance();
            }else{
                /*不是新郎新娘不可以进入洞房,这里注销掉,注销掉仍然算是一个线程到达*/
                phaser.arriveAndDeregister();
            }

        }

        @Override
        public void run() {

            arrive();

            eat();

            leave();

            hug();

        }
    }
}

运行结果

p1 到达现场! 
p3 到达现场! 

p2 到达现场! 

p4 到达现场! 

新郎 到达现场! 

新娘 到达现场! 

p0 到达现场! 


所有人到场7
p2 开始吃饭
p1 开始吃饭
新郎 开始吃饭
p3 开始吃饭
p0 开始吃饭
p4 开始吃饭
新娘 开始吃饭
所有人吃完7
p1 离开
p2 离开
p3 离开
p4 离开
新娘 离开
p0 离开
新郎 离开
所有人离场7
新郎 洞房新娘 洞房

新郎新娘抱抱2

进程已结束,退出代码0

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑白极客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值