当前位置: 首页 > news >正文

多线程进阶学习11------CountDownLatch、CyclicBarrier、Semaphore详解

CountDownLatch

①. CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞

②. 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)

③. 计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行

public static void main(String[] args) throws Exception {CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <= 6; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "\t");countDownLatch.countDown();}, i + "").start();}countDownLatch.await();System.out.println(Thread.currentThread().getName() + "\t班长关门走人,main线程是班长");}

比如说CountDownLatch你就可以回答:前面提到了CountDownLatch也是基于AQS实现的,它的实现机制很简单,

  • 当我们在构建CountDownLatch对象时,传入的值其实就会赋值给 AQS 的关键变量state
  • 执行CountDownLatch的countDown方法时,其实就是利用CAS 将state 减一
  • 执行await方法时,其实就是判断state是否为0,不为0则加入到阻塞队列中,将该线程阻塞掉(除了头结点),因为头节点会一直自旋等待state为0,当state为0时,头节点把剩余的在队列中阻塞的节点也一并唤醒。

再简单总结下:CountDownlatch基于AQS实现,会将构造CountDownLatch的入参传递至state,countDown()就是在利用CAS将state减-1,
await()实际就是让头节点一直在等待state为0时,释放所有等待的线程

CyclicBarrier

CyclicBarrier原理

1、它没有像CountDownLatch和ReentrantLock使用AQS的state变量,而是使用CyclicBarrier内部维护的内部维护count变量

2、同时CyclicBarrier借助ReentrantLock加上Condition实现等待唤醒的功能

parties变量和condition队列

在构建CyclicBarrier时,传入的值是parties变量,同时也会赋值给CyclicBarrier内部维护count变量(这是可以复用的关键)

//parties表示屏障拦截的线程数量,当屏障撤销时,先执行barrierAction,然后在释放所有线程
public CyclicBarrier(int parties, Runnable barrierAction)
//barrierAction默认为null
public CyclicBarrier(int parties)

每次调用await时,会将count -1 ,操作count值是直接使用ReentrantLock来保证线程安全性

如果count不为0,则添加则condition队列中

如果count等于0时,则把节点从condition队列添加至AQS的队列中进行全部唤醒,并且将parties的值重新赋值为count的值(实现复用)
在这里插入图片描述

再简单总结下:CyclicBarrier则利用ReentrantLock和Condition,自身维护了count和parties变量。 每次调用await将count-1,并将线程加入到condition队列上。等到count为0时,则将condition队列的节点移交至AQS队列,并全部释放。

阻塞任务线程而非主线程
CountDownLatch和CyclicBarrier都是线程同步的工具类。可以发现这两者的等待主体是不一样的。

  • CountDownLatch调用await()通常是主线程/调用线程
  • CyclicBarrier调用await()是在任务线程调用的,所以,CyclicBarrier中的阻塞的是任务的线程,而主线程是不受影响的。

比如集齐7颗龙珠就能召唤神龙。

  //集齐7颗龙珠就能召唤神龙
public class CyclicBarrierDemo {public static void main(String[] args) {// public CyclicBarrier(int parties, Runnable barrierAction) {}CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{System.out.println("召唤龙珠");});for (int i = 1; i <=7; i++) {final int temp=i;new Thread(()->{System.out.println(Thread.currentThread().getName()+"\t收集到了第"+temp+"颗龙珠");try {cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}).start();}}
}

共同抵达时的执行操作

屏障的抵达操作:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。

  • CyclicBarrier支持一个可选的Runnable barrierAction命令,在一组线程中的最后一个线程到达之后,但在释放所有线程之前运行一次。
  • 若在继续所有参与线程之前更新共享状态,此屏障操作很有用。

如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。

private static final ThreadPoolExecutor threadPool =new ThreadPoolExecutor(4, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。private static final CyclicBarrier cb = new CyclicBarrier(4, () -> System.out.println("寝室四兄弟一起出发去球场"));private static class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name + "开始从宿舍出发");try {cb.await();//线程的具体业务操作TimeUnit.SECONDS.sleep(1);System.out.println(name + "从楼底下出发");TimeUnit.SECONDS.sleep(1);System.out.println(name + "到达操场");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {String[] str = {"李明", "王强", "刘凯", "赵杰"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,现在开始打球");} catch (InterruptedException e) {e.printStackTrace();}}

屏障复用

CyclicBarrier是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。可重复利用,说明该类创建的对象可以复用;CyclicBarrier是一个同步工具类,它允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障(Barrier)。

现在对CyclicBarrier进行复用…又来了一拨人,看看愿不愿意一起打:

 private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。private static final CyclicBarrier cb = new CyclicBarrier(4, () -> System.out.println("寝室四兄弟一起出发去球场"));private static class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name + "开始从宿舍出发");try {cb.await();TimeUnit.SECONDS.sleep(1);System.out.println(name + "从楼底下出发");TimeUnit.SECONDS.sleep(1);System.out.println(name + "到达操场");} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {String[] str = {"李明", "王强", "刘凯", "赵杰"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,现在开始打球");System.out.println();System.out.println("现在对CyclicBarrier进行复用.....");System.out.println("又来了一拨人,看看愿不愿意一起打:");} catch (InterruptedException e) {e.printStackTrace();}String[] str1= {"王二","洪光","雷兵","赵三"};for (int i = 0; i < 4; i++) {threadPool.execute(new MyThread(str1[i]));}try {Thread.sleep(4000);System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");} catch (InterruptedException e) {e.printStackTrace();}}

Semaphore

Semaphore 是什么?Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。

使用场景:

通常用于那些资源有明确访问数量限制的场景,常用于限流 。

  • 比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。
  • 比如:停车场场景,车位数量有限,同时只能容纳多少台车,车位满了之后只有等里面的车离开停车场外面的车才可以进入。可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场了,直到有一辆车从停车场出去为止。
  • 比如,接口限流 ,应用限流 ,商品限流…

Semaphore常用方法说明

acquire()  
获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。tryAcquire(long timeout, TimeUnit unit)
尝试在指定时间内获得令牌,返回获取令牌成功或失败,不阻塞线程。release()
释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。hasQueuedThreads()
等待队列里是否还存在等待线程。getQueueLength()
获取等待队列里阻塞的线程数。drainPermits()
清空令牌把可用令牌数置为0,返回清空令牌的数量。availablePermits()
返回可用的令牌数量。

用semaphore 实现停车场提示牌功能

每个停车场入口都有一个提示牌,上面显示着停车场的剩余车位还有多少,当剩余车位为0时,不允许车辆进入停车场,直到停车场里面有车离开停车场,这时提示牌上会显示新的剩余车位数。

业务场景 :

  • 停车场容纳总停车量10。
  • 当一辆车进入停车场后,显示牌的剩余车位数响应的减1.
  • 每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。
  • 停车场剩余车位不足时,车辆只能在外面等待。
public class TestCar {//停车场同时容纳的车辆10private  static  Semaphore semaphore=new Semaphore(10);public static void main(String[] args) {//模拟100辆车进入停车场for(int i=0;i<100;i++){Thread thread=new Thread(new Runnable() {public void run() {try {System.out.println("===="+Thread.currentThread().getName()+"来到停车场");if(semaphore.availablePermits()==0){System.out.println("车位不足,请耐心等待");}semaphore.acquire();//获取令牌尝试进入停车场System.out.println(Thread.currentThread().getName()+"成功进入停车场");Thread.sleep(new Random().nextInt(10000));//模拟车辆在停车场停留的时间System.out.println(Thread.currentThread().getName()+"驶出停车场");semaphore.release();//释放令牌,腾出停车场车位} catch (InterruptedException e) {e.printStackTrace();}}},i+"号车");thread.start();}}
}

用semaphore 实现防止商品超卖

package com.limiting.semaphore;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;/*** 秒杀防止商品超卖现象*/
public class SemaphoreCommodity {//商品池private   Map<String, Semaphore> map=new ConcurrentHashMap<>();//初始化商品池public SemaphoreCommodity() {//手机10部map.put("phone",new Semaphore(10));//电脑4台map.put("computer",new Semaphore(4));}/**** @param name 商品名称* @return 购买是否成功*/public boolean getbuy(String name) throws Exception {Semaphore semaphore = map.get(name);while (true) {int availablePermit = semaphore.availablePermits();if (availablePermit==0) {//商品售空return  false;}boolean b = semaphore.tryAcquire(1, TimeUnit.SECONDS);if (b) {System.out.println("抢到商品了");///处理逻辑return  true;}}}public static void main(String[] args) throws Exception {SemaphoreCommodity semaphoreCommodity=new SemaphoreCommodity();for (int i = 0; i < 10; i++) {new Thread(()->{try {System.out.println(semaphoreCommodity.getbuy("computer"));} catch (Exception e) {e.printStackTrace();}}).start();}}
}

用semaphore 实现接口限流

切面注解SemaphoreDoc

package com.limiting.semaphore;import java.lang.annotation.*;@Documented@Target({ElementType.METHOD})//作用:方法
@Retention(RetentionPolicy.RUNTIME)
public @interface SemaphoreDoc {String key(); //建议设置不然可能发生,不同方法重复限流现象int limit() default 3;int blockingTime() default 3;}

切面类SemaphoreAop

@Component
@Aspect
public class SemaphoreAop {//这里需要注意了,这个是将自己自定义注解作为切点的根据,路径一定要写正确了@Pointcut(value = "@annotation(com.limiting.semaphore.SemaphoreDoc)")public void semaphoreDoc() {}//限流池private static  Map<String, Semaphore> map=new ConcurrentHashMap<>();@Around("semaphoreDoc()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {Object res = null;MethodSignature signature = (MethodSignature)joinPoint.getSignature();SemaphoreDoc annotation  = signature.getMethod().getAnnotation(SemaphoreDoc.class);int blockingTime = annotation.blockingTime();int limit = annotation.limit();String key = annotation.key();StringBuilder name = new StringBuilder(key+signature.getMethod().getName());//方法名for (String parameterName : signature.getParameterNames()) {name.append(parameterName);}Semaphore semaphore = map.get(name.toString());if (semaphore == null) {Semaphore semaphore1 = new Semaphore(limit);map.put(name.toString(),semaphore1);semaphore=semaphore1;}try {//获取令牌boolean b = semaphore.tryAcquire(blockingTime, TimeUnit.SECONDS);if (b) {//如果拿到令牌了那么执行方法try {res = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}} else {//在一定时间内拿不到令牌那么就访问失败throw  new Exception("访问超时,目前请求人数过多请稍后在试");}} finally {//释放令牌,腾出位置semaphore.release();}return  res;}}

相关文章:

多线程进阶学习11------CountDownLatch、CyclicBarrier、Semaphore详解

CountDownLatch ①. CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞 ②. 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞) ③. 计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行 public static void m…...

华为OD机试用java实现 -【RSA 加密算法】

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:RSA 加密算法 题目 RSA 加密…...

技术宅小伙:大龄程序员就业,未来我们将何去何从?

程序员是一个高薪高压的职业&#xff0c;同时也是一个需要不断学习的职业。随着技术的不断更新换代&#xff0c;程序员需要不断地学习新的知识和技能&#xff0c;以适应市场的需求。然而&#xff0c;有些程序员可能会遭遇裁员&#xff0c;失去了稳定的收入来源。有些程序员可能…...

Spring Boot+Vue实现Socket通知推送

目录 Spring Boot端 第一步&#xff0c;引入依赖 第二步&#xff0c;创建WebSocket配置类 第三步&#xff0c;创建WebSocket服务 第四步&#xff0c;创建Controller进行发送测试 Vue端 第一步&#xff0c;创建连接工具类 第二步&#xff0c;建立连接 ​编辑 第三步&a…...

python---python介绍

python介绍 1.1介绍 1.1.1为什么学习 1.1.2什么是python 优雅简单易学1.1.3在线2进制转换 在线二进制转文本工具 - 转换 1.1.4python的安装和配置 1.需要配置对应的环境变量。可以设置多个。 默认全选 设置安装的路径 最后安装完成即可。 验证&#xff1a;python 如何退出 1.1.…...

第十四届蓝桥杯大赛——真题训练第10天

目录 第一题&#xff1a;扫雷 题目描述 输入描述 输出描述 输入输出样例 运行限制 题目代码 第 2 题&#xff1a;完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 题目分析 题目代码 第三题&#xff1a;求阶乘 问题描述…...

3.29~3.30学习总结

刷题情况&#xff1a; 蓝桥杯刷题&#xff1a; Java学习情况: 抽象类&#xff1a;如果将子类中抽取的共性行为&#xff08;方法&#xff09;&#xff0c;子类的执行不一样的时候 &#xff08;通俗一点来说&#xff0c;就是无法找到一个万能的方法体供子类使用&#xff0c;但这…...

结构体详解 ——— C语言

目录 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体实现位段&#xff08;位段的填充&可移植性&#xff09; 位段的内存分配 1.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 结构体内存对…...

Java SE 基础(4) Java的语言特点

语言特点 Java是一门面向对象编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表&#xff0c;极好地实现了面向对象理论&a…...

都炸店了,拼多多还在坚持什么

子超这两天听说了拼多多被“炸店”事件&#xff0c;第一反应是震惊&#xff1a;这都什么年代了&#xff0c;还有这种不择手段的暴力行为&#xff1f;所谓的炸店&#xff0c;就是一些人员被煽动和组织起来&#xff0c;有预谋地对店铺发起打砸行动&#xff0c;这和线下去打砸商铺…...

vue尚品汇商城项目-day01【6.Footer组件的显示与隐藏】

文章目录6.Footer组件的显示与隐藏6.1我们可以根据组件身上的$route获取当前路由的信息&#xff0c;通过路由路径判断Footer显示与隐藏6.2配置路由的时候&#xff0c;可以给路由添加元信息[meta]&#xff0c;路由需要配置对象&#xff0c;它的key不能乱接、瞎写、胡写&#xff…...

命令行上的数据科学第二版 一、简介

原文&#xff1a;https://datascienceatthecommandline.com/2e/chapter-1-introduction.html 贡献者&#xff1a;Ting-xin 这本书是关于如何利用命令行做数据科学的。我的目标是通过教你使用命令行的力量&#xff0c;让自己成为一名更高效和多产的数据科学家。 在标题中同时使…...

utf-8转换到utf-16的转换过程你懂吗?

人生自是有情痴&#xff0c;此恨不关风与月。——唐代元稹《离思》 从UTF-8编码的文件中读取文本并将其存储到Java的String对象中&#xff0c;涉及到从字节序列到Unicode码点&#xff0c;再到UTF-16编码的转换。以下是详细的步骤&#xff1a; 从文件读取字节序列&#xff1a;首…...

C++编程大师之路:从入门到精通--持续更新中~

文章目录前言主要内容C基础入门初识C第一个C程序注释变量常量关键字标识符命名规则数据类型整型sizeof关键字实型&#xff08;浮点型&#xff09;字符型转义字符字符串型布尔类型 bool数据的输入运算符算术运算符赋值运算符比较运算符逻辑运算符程序流程结构选择结构if语句三目…...

面试阿里软件测试岗,收到offer后我却毫不犹豫拒绝了....

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是2年。我想说的是&#xff0c;但凡有点机会&#xff0c;千万别去外包&#xff01; 在深思熟虑过后&am…...

【c语言多线程编程】关于pthread_create()和pthread_join()的多线程详解

关于pthread_create()和pthread_join()的多线程详解 一、首先说一下pthread_create() 函数的用法&#xff1a; int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);各参数的含义&#xff1a; 1、pthread_t *thre…...

抖音seo矩阵系统源码搭建技术+二开开源代码定制部署

抖音已经成为了当今最为流行的短视频平台之一&#xff0c;拥有着庞大的用户群体和海量的视频资源。对于一些商家或者运营者来说&#xff0c;如何从这些视频资源中挖掘出有效的信息&#xff0c;进而提升自己的品牌、产品或者内容的曝光度&#xff0c;就成为了一个非常重要的问题…...

【周赛刷题】平衡树+图中最短环

2612. 最少翻转操作数&#xff08;平衡树&#xff09; 题目的难度有一部分在于数学推导。对于某个点 iii 进行反转是有一个范围的&#xff0c;这个范围需要考虑到边界的情况。可以的得到的一个结论是。对于窗口反转&#xff0c;KaTeX parse error: Expected group after ^ at p…...

C++笔记——第十篇 继承 的解析,详细易懂哦

目录 一、继承的概念及定义 1.继承的概念 2. 继承定义 2.1定义格式 2.2继承关系和访问限定符 2.3继承基类成员访问方式的变化 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承…...

SQL Server中的全文搜索

SQL Server中的全文搜索一、概述二、全文搜索查询三、将全文搜索查询与 LIKE 谓词进行比较四、全文搜索体系结构4.1、SQL Server 进程4.2、过滤器守护程序主机进程五、全文搜索处理5.1、全文索引过程5.2、全文查询流程六、全文索引体系结构6.1、全文索引结构6.2、全文索引片段6…...

自适应平移混音方法

一、简介&#xff1a; 自适应平移混音方法是一种常见的音频混音技术&#xff0c;它利用自适应滤波器对不同音频信号进行平移和加权&#xff0c;从而实现混音。 二、该方法的基本步骤如下&#xff1a; 采集和存储需要混音的音频信号。 对其中一个音频信号进行预处理&#xff0c…...

炼钢厂VR职业技能实训软件,提高员工学习效率和掌握技能速度

炼钢作业是一个高危、高压、高温的行业&#xff0c;在实际操作中需要严格遵守安全规范和操作规程&#xff0c;一旦出现差错可能造成巨大的经济损失和人员伤亡。 利用广州华锐互动开发的炼钢厂VR职业技能实训软件&#xff0c;可以有效帮助员工更好地理解和掌握炼钢作业中的相关…...

MySQL数据库范式

文章目录MySQL数据库范式1、范式的优缺点2、第一范式3、第二范式4、第三范式5、BC范式6、第四范式MySQL数据库范式 1、范式的优缺点 应用数据库范式的好处&#xff1a; 减少数据冗余&#xff08;这是最主要的好处&#xff0c;其他好处都是由此而附带的&#xff09;消除异常&…...

通过多层方法重塑网络安全

多年来&#xff0c;网络安全威胁的复杂性不断增加。此外&#xff0c;随着远程和混合工作场所模式的兴起&#xff0c;网络犯罪分子可以利用的漏洞数量显着增加。由于可能存在的网络威胁的范围如此之广&#xff0c;因此没有一种单一的解决方案可以应对所有威胁。 由于多种原因&a…...

Golang学习+深入(四)-运算符

目录 一、概述 1、算数运算符 2、关系运算符 3、逻辑运算符 4、赋值运算符 5、运算符优先级 6、位运算符 7、其他运算符 二、进制 1、进制转换 1、其他进制转十进制 2、十进制转其他进制 3、二进制转其他进制 4、其他进制转二进制 5、二进制在运算中的说明 三、…...

C++ 运算符重载:C++ 运算符重载的高级技巧和最佳实践

C 运算符重载&#xff1a;深入剖析与实现I. 引言A. 什么是运算符重载B. 为什么要使用运算符重载C. C运算符重载的优缺点II. 运算符重载基本概念A. 运算符重载的定义B. 运算符重载的分类1. 一元运算符2. 二元运算符C. 限制与规范1. 无法重载的运算符2. 重载运算符的规范与建议II…...

软件测试找了2个月了,找不到工作怎么办?

那就问你一些问题&#xff0c;看你能回答多少 1:测试流程是什么&#xff1f;测试用例包含哪些内容&#xff1f;测试用例设计都有哪些&#xff1f;给你一个一次性杯子&#xff0c;你会怎么测试&#xff1f; 2:数据库怎么查看前十行数据&#xff1f;内连接和外连接的区别&#…...

满足高并发的TB API接口接入说明

大家都知道&#xff0c;淘宝的反爬虫机制十分严&#xff0c;而很多时候&#xff0c;没办法高效的拿到数据内容响应终端需求&#xff0c;而依赖爬虫就会造成动不动就出现滑块验证&#xff0c;让人很无解。这里我们分享让采集不再出现任何滑块验证码&#xff0c;完全解密通过&…...

Themis Pro版将正式推出,3次迭代到底在酝酿什么?

最近在社区内讨论火热的Themis Pro&#xff0c;终于要来了&#xff01;4月2日Themis官网&#xff08;themis.capital &#xff09;全新升级改版上线&#xff0c;并宣布Themis Pro 即将于4月下旬正式推出。 Themis Pro 是基于Ve(3,3&#xff09;模型在FVM公链上搭建的新一代去中…...

边缘检测和轮廓检测

边缘检测 什么是边缘: * 图像中像素值发生剧烈变化的位置(高频信息区域) * 这些区域往往都是图像的边缘 方法:滤波、形态学处理等 边缘的作用 本质上,边缘是不同区域之间的边界。 其中包含了图像的区域信息,形状信息 一方面,可以利用这些信息来作为特征对图像进行理解(甚至…...