IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 深入理解AQS之Semaphore&CountDownLatch&Cyclic详解 -> 正文阅读

[Java知识库]深入理解AQS之Semaphore&CountDownLatch&Cyclic详解

目录

1.Semaphore介绍

1.1 限流操作实战

1.2 Semaphore源码解析

?2. countDownLatch介绍

2.1 多个线程等待实战

?2.2 主线程等待

2.3 源码分析


1.Semaphore介绍

semaphorer(信号量),是一个基于AQS框架实现的工具类,也是操作系统PV操作在java中的实现。通过发放许可来控制线程,只有拿到许可的线程才能执行代码,常用于限流操作。

PV操作是一种操作系统实现进程互斥与同步的有效方法:P表示通过,V表示释放。

P操作:S-1=X,如果X>=0线程执行,如果小于0放入等待队列中。

V操作:S+1=X,如果X>=0线程执行,如果小于0从等待队列释放一个等待线程。

构造器(默认非公平锁,也有公平锁实现):

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

常用的方法:

acquire()获取许可拿到共享锁

tryacquire()尝试获取锁,获取到锁返回true,没有返回false

release()释放许可

1.1 限流操作实战

模拟限流操作

1. 5个线程发送3个许可。

2. 10个核心线程发送5个许可。

import java.util.concurrent.Semaphore;

/**
 * 限流操作
 */
public class SemaphoreTest {

    private static Semaphore semaphore = new Semaphore(3);//发放三个许可

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

        //执行5个线程
        for(int i=0;i<5;i++){
            new Thread(()->{
                try{
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"开始购票");
                    Thread.sleep(5000);
                    System.out.println(Thread.currentThread().getName()+"购票成功");
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            }).start();

        }

    }
}
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 使用线程池-限流操作
 */
public class SemaphoreTest2 {

    //定义一个5个许可的信号量
    private static Semaphore semaphore = new Semaphore(5);

    //定义一个10个线程的线程池
    private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,20,60,
            TimeUnit.SECONDS,new LinkedBlockingQueue<>(200));

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

        //每100ms发送一次请求
        for(;;){
            Thread.sleep(100);
            threadPoolExecutor.execute(()->exec());
            System.out.println("-------------------------------------");
        }
    }

    private static void exec(){
        //模拟限流场景
        try{
            semaphore.acquire(1);
            //暂停2秒
            Thread.sleep(2000);
            System.out.println("执行业务逻辑");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            semaphore.release(1);
        }
    }
}

1.2 Semaphore源码解析

关注点:

1.加解锁逻辑(共享锁)实现

2.竞争锁失败入队操作阻塞和唤醒同步队列中等待线程逻辑

服了这代码了,写的狗屁不通

?

?

?2. countDownLatch介绍

?countDownLatch(闭锁)是一个同步协助类,用于一个或多个线程等待,相当于一个倒计时计数器,需要当计数器为0时才释放等待线程执行,等待线程会一次性全部返回,计数器不会重置,需要重置需要使用CyclicBarrier

?常用方法:

public void await() 让线程等待
public boolean await(long timeout, TimeUnit unit) 让线程等待一段时间,没有置为0执行业务
countDown() count减1

countDownLatch比join方法更加灵活,join方法底层在不停检测线程状态是否存活。

countDownLatch与CyclicBarrier区别:

1.countDownLatch只能使用一次,CyclicBarrier可以通过reset方法重复使用

2.countDownLatch是通过AQS的共享锁实现的,CyclicBarrier是通过reentrantlock的独占锁和condition实现线程唤醒。

2.1 多个线程等待实战

定义一个计数器为3的闭锁,线程挂起,等待2秒计数器减1,直到最后一个线程减到0跑线程

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

    //定义一个计数器为3的闭锁
    private static CountDownLatch countDownLatch = new CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException {
        for (int i =0 ;i<5;i++){
            //阻塞线程
            new Thread(()->{
                try{
                    countDownLatch.await();
                    System.out.println(Thread.currentThread().getName()+"执行业务");
                }catch(Exception e){
                    e.printStackTrace();
                }
            }).start();

            //等待2秒计数器减1
            Thread.sleep(2000);
            countDownLatch.countDown();
        }

    }
}

?2.2 主线程等待


import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest2 {

    public static void main(String[] args) throws InterruptedException {
        //1. 定义一个计数器为3的线程
        CountDownLatch countDownLatch = new CountDownLatch(3);

        //2.让线程计数器减1
        for(int i=0;i<3;i++){
            new Thread(()->{
                try{
                    Thread.sleep(1000);
                    countDownLatch.countDown();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }).start();
        }

        //3.主线程阻塞,当计数器为0时执行业务
        countDownLatch.await();
        System.out.println("执行业务");
    }
}

2.3 源码分析

关注点:

加解锁逻辑

1. countDownLatch.await()加锁:默认写死1永远waitStatus为-1走阻塞线程逻辑

2. countDownLatch.countDown()解锁:cas操作减1,直到为0执行唤醒线程逻辑?

解决synchronized两个线程导致的死锁问题:使用wait方法释放锁的形式解决,线上只能kill了

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:17:11  更:2022-05-11 16:20:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:51:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码