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
英文地址,請注明出處:http://en.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自檢系統的建…