JAVAEE--4.多线程案例

设计模式

1.单例模式

        1.1饿汉模式

        1.2懒汉模式(单线程版)

        1.3懒汉模式(多线程版本)

        1.4懒汉模式(多线程版本进阶版)

2.阻塞队列

3.定时器

4.线程池

1.单例模式

        设计模式是"软性约束",不是强制的,可以遵守也可以不遵守,按照设计模式写代码使代码不会太差

        框架是"硬性约束",必须遵守

        单例模式能保证某个类在程序中只保存唯一一个实例,而不会创建多个

        单例模式具体的实现方式有很多,最常见的是"饿汉"和"懒汉"两种

1.1饿汉模式

饿汉模式的饿代表急迫,一开始就把实例加载到内存中了

class singleton{private singleton(){};private static singleton instance=new singleton();public static singleton getInstance(){return instance;}
}
public class demo17 {public static void main(String[] args) {singleton s1=singleton.getInstance();singleton s2=singleton.getInstance();System.out.println(s1==s2);}
}

饿汉模式属于线程安全的,不涉及修改操作,只有读取操作

注释:1.private singleton(){};将构造方法设为私有,可以使得这个类无法new出来,更安全

        2.只有一个对外的接口,getInstance(){},更安全

        3.这个实例在程序加载的时候就被初始化了,属于类对象

1.2懒汉模式(单线程版)

懒汉模式的懒标识懒加载,提高效率

class singletonLazy{private singletonLazy(){};private static singletonLazy instance =null;public static  singletonLazy getInstance(){if(instance==null){instance=new singletonLazy();}return instance;}}
public class demo18 {public static void main(String[] args) {singletonLazy s1=singletonLazy.getInstance();singletonLazy s2=singletonLazy.getInstance();System.out.println(s1==s2);}
}

这里是单线程版本的懒汉模式,属于线程不安全

if(instance==null){

instance=new singletonLazy();

}

涉及到判定和修改操作,这个操作因为不是原子性的,所以会导致出现问题

此处实例的初始化并不是程序启动时,而是第一次调用方法的时候初始化实例

 会提升性能,减少加载时间                      

单例模式只能避免别人失误,无法应对别人的故意攻击(如:序列化反序列化,反射)

先判定后修改是常见的线程不安全问题

1.3懒汉模式(多线程版)

class singletonLazy{//多线程版本private singletonLazy(){};private static Object Locker=new Object();private static singletonLazy instance =null;public static  singletonLazy getInstance(){synchronized (Locker){if(instance==null){instance=new singletonLazy();}}return instance;}}
public class demo18 {public static void main(String[] args) {singletonLazy s1=singletonLazy.getInstance();singletonLazy s2=singletonLazy.getInstance();System.out.println(s1==s2);}
}

多线程版本需要考虑到线程的安全性,刚刚提到先修改后判定是不安全的,所以我们给它加上synchronize

把if和new包裹到一起

1.4懒汉模式(多线程版本进阶)

仅仅是上述代码依旧无法解决线程安全问题

上完锁后也可能会造成阻塞状态

会造成性能问题,所以可以在所外进行应该判定if(instance==null)
同时修改操作有可能造成内存可见性问题以及指令重排序问题,所以需要加上volatile关键字

class singletonLazy{//多线程版本进阶版private singletonLazy(){};private static Object Locker=new Object();private static volatile singletonLazy instance =null;public static  singletonLazy getInstance(){if(instance ==null){synchronized (Locker){if(instance==null){instance=new singletonLazy();}}}return instance;}}
public class demo18 {public static void main(String[] args) {singletonLazy s1=singletonLazy.getInstance();singletonLazy s2=singletonLazy.getInstance();System.out.println(s1==s2);}
}

2.阻塞队列

阻塞队列是一种特殊的队列,遵守"先进先出的"原则(线程安全+阻塞特性)

阻塞队列是一种线程安全的数据结构,并且具有以下的特点

1.当队列满的时候,继续入队列就会产生阻塞, 直到有其他线程从队列中取走元素

2.当队列为空的时候,继续出队列也会产生阻塞,直到有其他的线程从队列里插入元素

阻塞队列的一个典型的应用场景就是"生产者消费者模型",这是一种非常经典的开发模型

通常提到的"阻塞队列"是代码中的一个数据结构

但由于这个东西太好用了,以至于把这样的数据结构单独的封装成一个服务器程序

并且在单独的服务器上进行部署

此时,这样的阻塞队列,有了一个新的名字,"消息队列"(Message Queue,MQ)

A只和队列通信,B也只和队列通信

A不知道B的存在,代码中更没有B的影子

B也不知道A的存在,代码中没有A的影子

A的数据量激增的情况下,A往队列中写入的数据变快了,但是B任然可以按照原有的速度来消费数据,阻塞队列抗下这样的压力

生产者/消费者模型        

        这个模型的好处:

1.服务器之间的"解耦合"

                                模块之间的关联程度/影响程度

                                更希望见到的是低耦合

        

2.阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力(削峰填谷)   

问题:

1).为啥一个服务器,收到的请求更多,就可能会挂(可能会崩溃)

一台服务器就是一台电脑,上面提供了一些硬件资源(包括但不限于,CPU,内存,硬盘,网络带宽...)

就算你这个机器,配置的再好,硬件资源也是有限的

服务器每次收到一个请求,处理这个请求的过程都需要执行一系列的代码,在执行这些代码过程中,就会需要消耗一定的硬件资源"CPU,内存,硬盘,网络带宽"

这些请求消耗的总的硬件资源的量,超过了机器能提供的上限,那么此时机器就会出现问题(卡死,程序直接崩溃等)

2).为什么A和消息队列没有挂,只有B会挂

A的角色是一个"网关服务器",收到客户端的请求再把请求转发给其他服务器

这样的服务器里面的代码,做的工作比较简单(单纯的数据转发),消耗的硬件资源通常是更少

处理一个请求消耗的资源更少,同样的配置下,就能支持更多的请求来处理

同理,队列其实也是比较简单的程序,单位请求消耗的硬件资源也是比较少的

B这个服务器,是真正干活的服务器,要真正的完成一系列的业务逻辑

这一系列工作,代码量是非常的庞大,消耗的时间也是很多的,消耗的系统硬件资源也是更多的

类似的,MySQL这样的数据库处理每个请求的时候,做的工作就是比较多的,消耗的硬件资源也是比较多的,因此MySQL也是后端系统重容易挂的部分

对应的,像Redis这种内存数据库,处理请求做的工作远远少于MySQL做的工作,消耗的资源更少,Redis就比MySQL更不容易挂

代价:

1)需要更多的机器

2)A和B之间的通信的延时会变长

标准库中的阻塞队列

在java标准库中内置了阻塞队列,如果我们需要在一些程序中使用阻塞队列,直接使用标准库中的即可

BlockingQueue是一个接口,真正实现的类是Linked/Array/priorityBlockingQueue.

put方法用于阻塞式的入队列,take用于阻塞式的出队列

BlockingQueue也有offer,poll,peek等方法,但是这些方法不带有阻塞性质

生产者消费者模型

public class demo20 {//生产者消费者模型public static void main(String[] args) throws InterruptedException {BlockingQueue<Integer> queue =new ArrayBlockingQueue<>(1000);Thread thread1=new Thread(()->{//生产者int i=0;while(true){try {System.out.println("生产元素"+i);queue.put(i);i++;} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2=new Thread(()->{//消费者while(true){try {int num=queue.take();System.out.println ("消费元素"+num);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread1.start();thread2.start();thread1.join();thread2.join();}
}

阻塞队列的实现

1.通过"循环队列"的方式来实现

2.使用synchronized进行加锁控制

3.put插入元素的时候,判定如果队列满了,就进行wait.(注意,要在循环中进行wait,被唤醒时候不一定队列就不满了,因为同时是可能唤醒多个线程),以后只要出现wait,就使用while!!

4.take取出元素的时候,判定如果队列为空,就进行wait(也是循环wait)

class MyBlockingQueue{private String[] data=null;private volatile int head=0;private volatile int tail=0;private volatile int size=0;public MyBlockingQueue(int capacity){data=new String[capacity];}public void put(String s) throws InterruptedException {//生产者synchronized (this){while(size==data.length){this.wait(); //满了就不能再放了}data[tail]=s;size++;tail++;if(tail>=data.length){tail=0;}this.notify();}}public String take() throws InterruptedException {//消费者String s="";synchronized (this){while(data.length==0){this.wait();//如果没有内容的时候就阻塞}s=data[head];head++;size--;if(head>=data.length){head=0;}this.notify();}return s;}
}
public class demo21 {public static void main(String[] args) throws InterruptedException {MyBlockingQueue queue =new MyBlockingQueue(1000);Thread thread1=new Thread(()->{//生产者int i=0;while(true){try {System.out.println("生产元素"+i);queue.put(""+i);i++;} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread2=new Thread(()->{//消费者while(true){try {String num=queue.take();System.out.println ("消费元素"+num);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread1.start();thread2.start();thread1.join();thread2.join();}
}

3.定时器(Timer)

        定义:定时器也是软件开发的一个重要组件,类似于一个"闹钟",达到一个设定的时间之后,就执行某个制定好的代码

        定时器是一种实际开发中非常常用的组件

        比如网络通信中,如果对方500ms内没有返回数据,则会断开连接尝试重连

比如一个Map,希望里面的KEY在3s之后过期(自动删除)

类似于这样的场景就需要使用到定时器

标准库中的定时器

标准库中提供了Timer类,Timer类的核心方法是schedule

schedule 包含两个参数,第一个参数指定即将要执行的任务代码,第二个参数指定多长时间之后执行(单位为毫秒)

import java.util.Timer;
import java.util.TimerTask;public class demo24 {public static void main(String[] args) {//使用定时器Timer timer=new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world3");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world1");}},1000);}
}

结果:

模拟实现定时器

class MyTimerTask implements Comparable<MyTimerTask>{Runnable runnable;long time;public MyTimerTask(Runnable runnable,long delay){this.runnable=runnable;this.time=delay+System.currentTimeMillis();}public long  getTime(){return time;}public void run(){runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}
}

Task类用来描述一个任务(作为Timer的内部类),里面包含一个runnable和一个time(毫秒时间戳)

这个对象需要放在优先级队列中,因此需要实现Comparable接口

class MyTimer{private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();private  Object Locker=new Object();MyTimer(){Thread thread=new Thread(()->{try {while(true){synchronized (Locker){while(queue.isEmpty()){//如果为空的时候就进入阻塞Locker.wait();}MyTimerTask currentmyTimeTask=queue.poll();if(System.currentTimeMillis()>=currentmyTimeTask.getTime()){currentmyTimeTask.run();}else{Locker.wait(currentmyTimeTask.getTime()-System.currentTimeMillis());queue.offer(currentmyTimeTask);}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}public void schedule(Runnable runnable,long delay){synchronized (Locker){MyTimerTask myTimerTask=new MyTimerTask(runnable,delay);queue.offer(myTimerTask);Locker.notify();}}
}

Timer实例中,通过priorityQueue来组织若干个Task对象

通过schedule来往队列中插入一个个Task对象

Timer类中存在一个worker线程,一直不停的扫描队首元素,看看是否能执行这个任务

全部代码:

import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;class MyTimerTask implements Comparable<MyTimerTask>{Runnable runnable;long time;public MyTimerTask(Runnable runnable,long delay){this.runnable=runnable;this.time=delay+System.currentTimeMillis();}public long  getTime(){return time;}public void run(){runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}
}
class MyTimer{private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();private  Object Locker=new Object();MyTimer(){Thread thread=new Thread(()->{try {while(true){synchronized (Locker){while(queue.isEmpty()){//如果为空的时候就进入阻塞Locker.wait();}MyTimerTask currentmyTimeTask=queue.poll();if(System.currentTimeMillis()>=currentmyTimeTask.getTime()){currentmyTimeTask.run();}else{Locker.wait(currentmyTimeTask.getTime()-System.currentTimeMillis());queue.offer(currentmyTimeTask);}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}public void schedule(Runnable runnable,long delay){synchronized (Locker){MyTimerTask myTimerTask=new MyTimerTask(runnable,delay);queue.offer(myTimerTask);Locker.notify();}}
}
public class demo25 {//模拟实现定时器public static void main(String[] args) {MyTimer myTimer=new MyTimer();myTimer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world3");}},3000);myTimer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world2");}},2000);myTimer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world1");}},1000);}
}

 定时器的构成:

1.一个带优先级队列(不要使用PriorityBlockingQueue,容易死锁)

2.队列中每一个元素是一个Task对象\

3.Task中带有一个时间属性,队首元素就是要执行的任务

4.同时有一个worker线程一直扫描队首元素,看队首元素是否需要执行

业界实现定时器,除了基于优先级队列的方法之外,还有一个经典的实现方式,"时间轮" ,它也是一个巧妙设计的数据结构

定时器这个东西特别重要,特别常用,尤其是后端开发,和"阻塞队列"类似,也会有专门的服务器,就是用来在分布式系统中实现定时器这样的效果

hashmap  => redis

阻塞队列  => 消息队列

定时器  => 定时任务

4.线程池(ThreadPoolExecutor)

最初引入线程是因为线程太重了,频繁的创建销毁进程,开销大

随着业务上对于性能的要求越来越高,线程创建/销毁的频次越来越多,此时线程创建销毁的开销变的比较明显,无法忽略不计了

线程池就是解决上述问题的常见方案

线程池就是把线程提前从系统中申请好,放到一个地方

后面需要使用线程的时候,直接从这个地方来取,而不是系统重新申请

线程用完了之后,也是还是回到刚才的地方

"池"---->本质上是为了提高程序的效率

内核态&用户态

操作系统=操作系统内核态+操作系统配套的应用程序

操作系统内核-->操作系统的核心功能部分,负责完成一个操作系统的核心工作(管理)

包括为软件提供稳定的运行环境和管理硬件

对应的,执行的很多代码逻辑都是要用户态的代码和内核态的代码配合完成的

应用程序有很多,这些应用程序都是由内核统一负责管理和服务

内核里的工作十分的繁忙--->所以提交给内核要做的任务可能是不可控的

        从系统创建线程,就相当于让银行的人给我银钱->这样的逻辑就是调用系统api,由系统内核执行一系列逻辑来完成这个过程

        直接从线程池里取,这就相当于是自助复印,整个过程都是纯用户态代码,都是自己控制的,整个过程更可控,效率更高

因此通常认为,纯用户态操作,就比经过内核态操作的效率更高

java标准库中提供了现成的线程池

标准库提供了类ThreadPoolExcutor(构造方法有很多的参数)

带入的包是java.util.concurrent   concurrent的意思是并发

它的构造方法中有7个参数

1.corePoolSize:核心线程数-------正式员工的数量(正式员工,一旦录用,永不辞退)(最少有多少个)

2.maximunPoolSize:最大线程数---正式员工+临时员工的数量(临时工:一段时间不干活,就会被辞退)

最大线程数=核心线程数+非核心线程数

如果核心线程数和最大线程数一样的话,将不会自动扩容

核心线程会始终存在于线程池内部

非核心线程会在繁忙的时候被创建出来,不繁忙了,就会把这些线程真正的释放掉

3.keepAliveTimer:临时工允许的空闲时间

4.unit:keepAliveTime的时间单位,可以是分,是秒,或者是其他

5.workQueue:工作队列--传递任务的阻塞队列,可以传递你的任务Runnable

6.threadFactory:创造线程的工厂,参与具体的线程创建工作,通过不同线程工厂创建出的线程相当于对一些属性进行了不同的初始化设置

"工厂"是指"工厂设计模式"也是一种常见的设计模式

工厂设计模式是一种在创建类的实力时使用的设计模式,由于构造方法有"坑",通过工厂设计模式来填坑

就是Thread类的工厂类,通过这个类完成Thread的实例创建和初始化操作,此时ThreadFactory就可以针对线程池里的线程进行批量的设置属性,此处一般不会进行调整,就使用标准库提供的ThreadFactory的默认值就可以了---->ThreadFactory threadFactory

7.RejectedExecutionHandler:拒绝策略,如果任务量超过公司的负荷该如何处理

         7.1)AbortPolicy:超过负荷直接抛出异常

        7.2)CallerRunsPolicy():调用者负责处理多出来的任务

        7.3)DiscardOldestPolicy():丢弃队列中最老的任务

        7.4)DiscardPolicy():丢弃新来的任务

标准库中的线程池

1.使用Executors.newFixedThreadPool(10)能创建出固定包含十个线程的线程池

2.返回值类型为ExecutorService 

3.通过ExecutorService.submit 可以注册一个任务到线程池中

public class demo22 {public static void main1(String[] args) {ExecutorService service= Executors.newFixedThreadPool(4); // Executors的本质是ThreadPoolExecutor类的封装//返回的类型是ExecutorServiceservice.submit(new Runnable() {//此处也可以使用lambda@Overridepublic void run() {System.out.println("Hello World!");}});service.shutdown();//service是前端线程,得主动结束}public static void main(String[] args) {ExecutorService service= Executors.newFixedThreadPool(4); // Executors的本质是ThreadPoolExecutor类的封装//返回的类型是ExecutorServiceservice.submit(()->{//此处可以使用lambdaSystem.out.println("Hello World!");});service.shutdown();//service是前端线程,得主动结束}
}

Executors创建线程池的几种方式

1.newFixedThreadPool:创建固定线程数的线程池

核心线程数和最大线程数一样,所以不会扩容

2.newCachedThreadPool:创建线程数目动态增长的线程池

设置了非常大的线程数,自动扩容

3.newSingleThreadExcutor:创建只包含单个线程的线程池

4.newScheduledThreadPool:设定延迟时间后执行命令,或定期执行命令,是进阶版的Timer

Executors的本质上是ThreadPoolExecutor类的封装

这里的i报错了,涉及的知识点是变量捕获,只能捕获final或者事实final

可以

创建一个新的变量id,来得到i的值,并且id是不会改变的,这样即可

问题:使用线程池的时候,需要指定线程个数,线程个数如何指定,指定多少合适?

        实际开发中,更建议的做法是通过实验的方式找到一个合适的线程池的个数的值

        给线程池设置不同的线程数,分别进行性能测试是,关注响应时间/消耗的资源指标,挑选一个比较合适的数值

线程池的模拟实现

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class myThreadPool{BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>(1000);public myThreadPool(int n){for(int i=0;i<n;i++){Thread thread=new Thread(()->{while(true){try {Runnable runnable =queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();}}public void sunbmit(Runnable runnable){try {queue.put(runnable);} catch (InterruptedException e) {e.printStackTrace();}}
}
public class demo23 {public static void main(String[] args) {myThreadPool myThreadPool=new myThreadPool( 4);for(int i=0;i<1000;i++){int id=i;myThreadPool.sunbmit(()->{System.out.println("执行任务"+id+", "+Thread.currentThread().getName());});}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91354.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

量化感知训练(QAT)流程

WHAT&#xff1a;量化感知训练&#xff08;Quantization-Aware Training, QAT&#xff09; 是一种在模型训练阶段引入量化误差的技术。它的核心思想是&#xff1a;通过在前向传播时插入“伪量化节点”引入量化误差&#xff0c;将权重和激活模拟为低精度&#xff08;如 int8&…

docker 用于将镜像打包为 tar 文件

docker save 是 Docker 中用于将镜像打包为 tar 文件的命令&#xff0c;常用于镜像的备份、迁移或离线传输。以下是其核心用法和注意事项&#xff1a;一、基本语法bashdocker save [选项] IMAGE [IMAGE...] > 文件名.tar # 或 docker save -o 文件名.tar IMAGE [IMAGE...]IM…

设计模式(六)创建型:单例模式详解

设计模式&#xff08;六&#xff09;创建型&#xff1a;单例模式详解单例模式&#xff08;Singleton Pattern&#xff09;是 GoF 23 种设计模式中最简单却最常被误用的创建型模式。其核心价值在于确保一个类在整个应用程序生命周期中仅存在一个实例&#xff0c;并提供一个全局访…

PostgreSQL AND OR 操作符详解

PostgreSQL AND & OR 操作符详解 在数据库查询中,AND 和 OR 是两种常见的逻辑操作符,用于组合多个查询条件。PostgreSQL 作为一款功能强大的开源关系型数据库管理系统,同样支持这些操作符。本文将详细介绍 PostgreSQL 中的 AND 和 OR 操作符,并探讨它们在查询中的应用…

RabbiteMQ安装-ubuntu

Ubuntu 1.安装Erlang RabbitMQ需要Erlang语言的支持&#xff0c;在安装RabbitMQ之前需要安装Erlang #更新软件包 sudo apt-get update#安装erlang sudo apt-get install erlang查看erlang版本 roothcss-ecs-027f:/# erl Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [sm…

Linux驱动20 --- FFMPEG视频API

目录 一、FFMPEG 视频 API 的使用 1.1 介绍 1.2 整体编程过程 获取核心上下文指针 打开输入流文件 获取输入流 获取编码器 初始化解码器 申请输出流指针 获取显示数据空间大小 申请输出显示空间 绑定输出流和输出显示空间 申请格式转换上下文 申请输入流指针 读取一帧数据 发…

OpenBayes 一周速览丨Self Forcing 实现亚秒级延迟实时流视频生成;边缘AI新秀,LFM2-1.2B采用创新性架构超越传统模型

公共资源速递 This Weekly Snapshots &#xff01; 5 个公共数据集&#xff1a; * AF-Chat 音频对话文本数据集 * ArtVIP 机器交互式图像数据集 * Updesh 印度语合成文本数据集 * Medical Information 药品信息数据集 * Nemotron-Math-HumanReasoning 数学推理数据集…

[NOIP2002 提高组] 均分纸牌

题目描述有N堆纸牌&#xff0c;编号分别为 1,2,…,N。每堆上有若干张&#xff0c;但纸牌总数必为N的倍数。可以在任一堆上取若干张纸牌&#xff0c;然后移动。移牌规则为&#xff1a;在编号为1堆上取的纸牌&#xff0c;只能移到编号为2的堆上&#xff1b;在编号为N的堆上取的纸…

【音视频】WebRTC-Web 音视频采集与播放

一、打开摄像头 打开摄像头首先需要有一个html的video标签&#xff1a; id "local-video"&#xff0c;是为了后续的js脚本调用这个对象autoplay是设置打开后自动播放&#xff0c;playsinline则是为了兼容移动端 <video id "local-video" autoplay p…

数据治理平台如何选?深度解析国产化全栈方案与行业落地实践

“数据治理平台厂商有哪些&#xff1f;”国内主流厂商包括阿里云、华为、百分点科技等&#xff0c;各有所长。其中&#xff0c;百分点科技凭借在应急管理、智慧公安及央国企数字化领域的深度实践&#xff0c;打造了行业特色鲜明的数据治理解决方案。百分点科技的数据治理解决方…

限流算法详解:固定窗口、滑动窗口、令牌桶与漏桶算法全面对比

限流&#xff08;Rate Limiting&#xff09;是保障系统稳定性和服务质量的关键机制&#xff0c;尤其在高并发、突发流量、攻击防护等场景中至关重要。本文将详细介绍四种主流限流算法&#xff1a;固定窗口&#xff08;Fixed Window&#xff09;滑动窗口&#xff08;Sliding Win…

Sentinel 搭建应用层面与网关层面的流控保护

源码&#xff1a;妖精的尾巴/spring-cloud-alibaba Nacos 和 Sentinel Dashboard 我这里全是使用window 本地运行的&#xff0c;需要自行下载运行 服务层面&#xff1a; 当你在某个具体的服务上使用Sentinel时&#xff0c;更多的是关注该服务内部资源的保护。例如&#xff0c…

纯血鸿蒙 AudioRenderer+AudioCapturer+RingBuffer 实现麦克风采集+发声

总共两个类&#xff0c;放到代码里&#xff0c;就可以快速完成K歌的效果&#xff0c;但应用层这么做延迟是比较高的&#xff0c;只是做一个分享。 类代码 import { audio } from kit.AudioKit; import { BusinessError } from kit.BasicServicesKit; import { AudioBufferFlow,…

洛谷 P1601 A+B Problem(高精)普及-

题目描述 高精度加法&#xff0c;相当于 ab problem&#xff0c;不用考虑负数。 输入格式 分两行输入。a,b≤10500a,b \leq 10^{500}a,b≤10500。 输出格式 输出只有一行&#xff0c;代表 ababab 的值。 输入输出样例 #1 输入 #1 1 1输出 #1 2输入输出样例 #2 输入 #2 1001 909…

Matrix Theory study notes[6]

文章目录linear spacereferenceslinear space a basis of linear space VkV^kVk,which is x1,x2,...xkx_1,x_2,...x_kx1​,x2​,...xk​,can be called as a coordinate system.let vector v∈Vkv \in V^kv∈Vk and it can be linear expressed on this basis as va1x1a2x2...…

专线与专线之间的区别

下面我们从定义、技术特点、适用场景、优缺点等多个维度来详细对比&#xff1a;✅ 一、四种方案简要定义技术方案定义MPLS 专线运营商基于 MPLS 技术提供的私有虚拟网络&#xff0c;逻辑隔离、安全可靠VPN over Internet利用公网加密通道&#xff08;如IPSec&#xff09;构建虚…

Git工作流:团队协作的最佳实践

目录 一、什么是 Git 工作流&#xff1f;为什么需要它&#xff1f; 二、基础&#xff1a;Git 分支核心概念 三、主流 Git 工作流实战指南 1. 集中式工作流&#xff08;Centralized Workflow&#xff09;&#xff1a;适合小团队 / 新手 操作步骤&#xff1a; 优缺点&#…

算法竞赛阶段二-数据结构(35)数据结构单链表模拟实现

//链表--链式存储的线性表 //存信息和下一个节点位置&#xff0c;数据域和指针域合起来叫节点 //带头&#xff08;哨兵位&#xff09;下标为0 //单向&#xff0c;双向&#xff0c;循环链表 //实现 单 //俩足够大数组 // elem&#xff0c;数据域 // next &#xff0c;指针域…

《Computational principles and challenges in single-cell data integration》

1. 引言&#xff1a;单细胞数据整合的背景与重要性单细胞基因组学技术&#xff08;如scRNA-seq、scATAC-seq等&#xff09;近年来快速发展&#xff0c;能够以单细胞分辨率揭示细胞异质性和分子机制。然而&#xff0c;不同实验、样本和数据模态&#xff08;如RNA表达、DNA甲基化…

蔚来汽车携手通义灵码入选 2025 世界人工智能大会标杆案例

7月28日&#xff0c;在2025年世界人工智能大会上&#xff0c;通义灵码助力蔚来汽车研发效能升级成功入选2025年“人工智能”行业标杆案例荟萃。蔚来汽车已有近 1000 名工程师常态化使用通义灵码&#xff0c;AI 生成代码占比超 30%&#xff0c;尤其在蔚来“天探”AI自检系统的建…