多線程的代碼案例

目錄

單例模式

餓漢模式

懶漢模式

阻塞隊列

?生產者消費者模型意義:

阻塞隊列使用方法

實現阻塞隊列

阻塞隊列實現生產者消費者模型

定時器

實現簡單的定時器

工廠模式

線程池

為啥呢? 從池子里面取 比 創建線程 效率更高

線程池的創建

怎么填坑

ThreadPoolExecutor

線程數目設置

?實現線程池

?小結


兩個設計模式: 單例模式, 工廠模式

單例模式

有些場景中希望有些類僅僅創建一個對象, 代碼中很多管理數據的對象都是單例的, MySQL JDBC等.

人可能會出錯, 需要編譯器幫我們做出監督. 就比如 @Override 必須是方法重寫.,在語法層面上沒有對單例做出支持, 只能通過編程技巧實現

餓漢模式

剛開始就創建了實例舉個例子:

//期望這個類能有唯一實例
class Singleton {//設置為靜態變量在 Singleton 類被加載時會創建實例private static Singleton instance = new Singleton();//獲取實例public static Singleton getInstance() {return instance;}//把構造方法設為 私有 , 類外面的代碼無法 new 出類對象了.private Singleton() {};
}

注意:

1> 在類的內部提供線程的實例

2> 把構造方法設為 private ,避免其他代碼創建實例.

懶漢模式

先判斷是否需要創建實例舉個例子:

//期望這個類能有唯一實例
class SingletonLazy {private static volatile SingletonLazy instance = null;//獲取實例public static SingletonLazy getInstance() {if(instance == null) {synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}//把構造方法設為 私有 , 類外面的代碼無法 new 出類對象了.private SingletonLazy() {};
}

注意:

1> 第一次判斷是否為空原因:

因為加鎖開銷很大, 而且可能涉及到鎖沖突, 所以我們增加一次判斷, 不為空直接返回 instance

2> 加鎖的原因:

在本操作中會出現讀取和修改的操作, 會出現兩個都判斷為空后創建多個實例的情況.

3> 使用 volatile 原因:

指令重排序問題

編譯器為了提高效率, 可能調整代碼的執行順序, 但是必須保持代碼邏輯不變, 單線程沒問題, 但是多線程可能有問題.

new 操作, 可能觸發指令重排序

new 操作分為三步:

1.?申請內存空間

2. 在內存空間上構造對象

3. 把內存地址給 instance

可能按照 123, 132順序執行, 1一定先執行

在多線程下, 假設? t1線程? ?按照1 3 2 的順序? 執行1? 3后, instance非空指向一個沒初始化的非法對象, 這時? ? ??t2線程? ?在判斷instance 不為空后, 直接返回一個非法對象, 導致出現bug

使用 volatile 保證不會出現指令重排序問題

阻塞隊列

多線程代碼中比較常用到的一種數據結構

特殊的隊列

1> 線程安全

2> 帶有阻塞特性

a) 如果隊列為空, 繼續出隊列, 就會發生阻塞, 阻塞到其他線程往隊列里添加元素位置為止

b) 如果隊列為滿, 繼續入隊列, 也會發生阻塞, 阻塞到其他線程從隊列中取走元素位置為止.

意義: 實現 " 生產者消費者模型 " 一種常見的多線程代碼編寫方式

舉個例子: 包餃子

1> 每個人分別負責搟餃子皮和包餃子

2> 當搟餃子皮快了 就會在 放餃子皮的蓋簾滿的時候停下來等包餃子的

3> 當包餃子快了 就會停下來等 搟餃子皮的

蓋簾就相當于阻塞隊列

生產者 把生產出來的內容放到阻塞隊列中

消費者 從阻塞隊列中獲取元素

?生產者消費者模型意義:

1> 解耦合

兩個模塊聯系越緊密, 耦合就越高, 這個模型讓耦合降低

2> 削峰填谷

服務器 A 給服務器 B發起請求, 不同服務器消耗的硬件資源不一樣, A收到的請求發給B可能就掛了.使用削峰填谷讓 B 接受的請求按照 B 的原有節奏處理情況.(這種情況一般不會持續存在, 就好比學校搶課的情況), 峰值過后 B把積壓的數據處理掉

阻塞隊列使用方法

在 Java 標準庫里, 已經提供了現成的 阻塞隊列直接使用

在標準庫里, 針對 BlockingQueue 提供了兩種最重要的實現方式

1> 基于數組

2> 基于鏈表

BlockingQueue 一般不適用 Queue 中的一些方法, 因為他們不具備阻塞的特性.?

一般使用 (put 阻塞式的入隊列), (take 阻塞式的出隊列)

示例:?

public class Test {public static void main(String[] args) throws InterruptedException {BlockingDeque<String> queue = new LinkedBlockingDeque<>();queue.put("111");queue.put("222");queue.put("333");queue.put("444");String elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);}
}

最后一次輸出時發生了阻塞.

實現阻塞隊列

基于普通隊列加上阻塞和線程安全

普通隊列基于數組 或者 基于鏈表

基于數組實現隊列理解成一個環

class MyBlockingQueue {private String[] data = new String[1000];// 隊列的起始位置private int head = 0;// 隊列的結束位置的下一個位置private int tail = 0;//隊列中有效元素的個數private int size = 0;//提供的方法 入隊列 出隊列public void put(String elem) throws InterruptedException {synchronized (this) {while(size == data.length) {this.wait();}data[size] = elem;tail++;if(tail == data.length) {tail = 0;}size++;//這個 notify 用來喚醒 take 中的 waitthis.notify();}}public String take() throws InterruptedException {synchronized (this) {while(size == 0) {this.wait();}String ret = data[head];head++;if(head == data.length) {head = 0;}size--;//這個 notify 用來喚醒 put 中的 waitthis.notify();return ret;}}
}

wait 除了可以用 notify 喚醒, 還可以用 interrupt 喚醒, 直接整個方法結束了, 因為使用了 throws 拋出異常, 這是沒有什么事

如果使用 try catch 方式就會出現bug, 讓 tail 把指向的元素覆蓋掉了, 然后弄丟了一個元素, 而且 size 也會比數組最長長度還大.(此處不理解看http://t.csdnimg.cn/OBwXN?-->中斷一個線程目錄)

所以在wait 返回的時候進一步確認是否當前隊列是滿的不是, 如果是滿的繼續進行wait

所以直接使用 while 判定是否是滿的.

為了避免內存可見性問題, 把 volatile 加好

阻塞隊列實現生產者消費者模型

package Demo2;import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;class MyBlockingQueue {private String[] data = new String[1000];// 隊列的起始位置private volatile int head = 0;// 隊列的結束位置的下一個位置private volatile int tail = 0;//隊列中有效元素的個數private volatile int size = 0;//提供的方法 入隊列 出隊列public void put(String elem) throws InterruptedException {synchronized (this) {while(size == data.length) {this.wait();}data[tail] = elem;tail++;if(tail == data.length) {tail = 0;}size++;//這個 notify 用來喚醒 take 中的 waitthis.notify();}}public String take() throws InterruptedException {synchronized (this) {while(size == 0) {this.wait();}String ret = data[head];head++;if(head == data.length) {head = 0;}size--;//這個 notify 用來喚醒 put 中的 waitthis.notify();return ret;}}
}public class Test {public static void main(String[] args) {MyBlockingQueue queue = new MyBlockingQueue();// 消費者Thread t1 = new Thread(() -> {while(true) {try {String result = queue.take();System.out.println("消費元素: " + result);Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 生產者Thread t2 = new Thread(() -> {int num = 1;while(true) {try {queue.put(num+ " ");System.out.println("生產元素: " + num);num++;} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

定時器

約定一個時間, 時間到達之后執行某個代碼邏輯, 在網絡通信中很常見

?在 標準庫 中有現成定時器的實現

    public static void main(String[] args) {Timer timer = new Timer();// 給定時器安排了一個任務, 預定在 xxx 時間去執行timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("執行定時器任務");}}, 2000);System.out.println("程序啟動!");}

使用匿名內部類的寫法繼承 TimerTask 創建出實例, 目的時重寫 run, 描述任務的詳細情況

當前代碼也是多線程, timer 里面包含一個線程, 下圖是運行結果

可以發現整個進程沒有結束, 因為 Timer 內部的線程阻止了進程結束.

?Timer 里面可以安排多個任務.

    public static void main(String[] args) {Timer timer = new Timer();// 給定時器安排了一個任務, 預定在 xxx 時間去執行timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("3000");}}, 3000);System.out.println("程序啟動!");timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000");}}, 2000);System.out.println("程序啟動!");timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1000");}}, 2000);System.out.println("程序啟動!");}

實現簡單的定時器

1> Timer 中需要有一個線程, 掃描任務是否到時間了, 可以執行了

2> 需要一個數據結構把所有任務保存起來(使用優先級隊列)?

3> 創建一個類, 通過類的對象描述一個任務(至少要包含任務內容和時間)

?其中需要記錄, 絕對的時間.

import java.awt.*;
import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;// 通過這個類, 描述一個任務
class MyTimerTask implements Comparable<MyTimerTask> {// 執行的任務private Runnable runnable;// 執行任務的時間private long time;// 此處的 delay 就是 schedule 方法傳入的 "相對時間"public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}@Overridepublic int compareTo(MyTimerTask o) {// 讓隊首元素是最小時間的值return (int) (this.time - o.time);// 讓隊首元素是最大時間的值//return (int) (o.time - this.time);}public long getTime() {return time;}public Runnable getRunnable() {return runnable;}
}// 自己的定時器
// 添加元素和掃描線程是不同線程操作同一個隊列, 需要加鎖 <--原因之一
class MyTimer {// 使用一個數據結構, 保存所有的任務private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();// 使用這個對象作為鎖對象private Object locker = new Object();public void schedule(Runnable runnable, long delay) {synchronized(locker) {queue.offer(new MyTimerTask(runnable, delay));locker.notify();}}// 掃描線程public MyTimer() {// 創建一個掃描線程Thread t = new Thread(() -> {// 掃描線程需要不停掃描看是否到達時間while (true) {try {synchronized (locker) {// 不要使用 if 作為 wait 的判定條件, 應使用while// 使用 while 是為了在喚醒之后 在再次確認一下條件while (queue.isEmpty()) {locker.wait();}MyTimerTask task = queue.peek();// 比較一下當前的隊首元素是否可以執行了long curTime = System.currentTimeMillis();if (curTime >= task.getTime()) {// 執行任務task.getRunnable().run();//執行完了, 就從隊列中刪除queue.poll();} else {// 不可執行, 先等著, 等待下一輪的循環判定locker.wait(task.getTime() - curTime);}}}catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}public class Demo2 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000");}}, 1000);}
}

工廠模式

線程池

線程創建/銷毀 比 進程快, 但是進一步提高創建/銷毀的頻率, 線程的開銷也不能忽視了

兩種提高效率的方法:

1> 協程 (輕量級線程)?

相對于線程, 把系統調度的過程給忽略了,(程序猿手動調度), 當下比較流行(Java 標準庫沒有協程)

2> 線程池

兜底, 使線程不至于很慢

例子: 我是個妹子, 在談男朋友, 一段時間后, 我不想和他好了, 就冷暴力然后分手, 分手之后再去找另一個小哥哥, 然后和另一個小哥哥好上了.?

線程池就是我在談第一個男朋友的時候就同時和其他小哥哥搞曖昧(培養感情), 哪天想分手了直接分, 然后無縫銜接

線程池: 在使用第一個線程的時候, 提前把 2, 3, 4, 5線程創建好(培養感情), 后續想使用新的線程不必創建, 直接使用(創建線程的開銷降低了)

為啥呢? 從池子里面取 比 創建線程 效率更高

從池子里取, 就是純粹用戶態操作

創建新的線程需要 用戶態 + 內核態 相互配合 完成

操作系統是由 內核 + 配套的應用程序 構成

內核 是系統最核心的部分, 創建線程操作需要調用系統 api, 進入到內核中, 按照內核態的方式來完成一系列動作

當你想要創建線程的時候, 內核需要給所有進程提供服務, 不可控, 難以避免會做一些其他的事導致效率減低

線程池的創建

Java標準庫提供了寫好的線程池.

創建線程池對象并沒有 new , 而是通過專門的方法返回了一個線程池對象(工廠模式), 通常創建對象使用 new , new 就會觸發類的構造方法, 但構造方法存在一定的局限性. 工廠模式是給構造方法填坑的.

怎么填坑

我們構造一個對象希望有多種構造方式, 這就需要多個構造方法, 但是構造方法的名字必須是類名, 不同的構造方法只能通過 重載區分, 但是如果實現方法不一樣, 但是參數類型/個數一樣咋辦呢?

使用工廠設計模式, 使用普通的方法代替構造方法完成初始化工作, 普通方法使用名字區分.

?Executors 是一個 工廠類, newCachedThreadPool 是工廠方法, 使用靜態方法通過類名調用

工廠方法有很多, 上述方法創建出來的線程池對象的線程數目可以動態適應, 隨著王線程池里面添加任務, 線程池中的線程自動創建, 創建出來在池子里保留一定時間以備后續使用.

這個方法是固定的線程池, 調用方法時手動指定創建幾個線程

?還用很多其他線程池上面介紹的兩種用的更多一點

ThreadPoolExecutor

上述工廠方法生成的線程池本質上是對 類(ThreadPoolExecutor) 的封裝

核心方法:

1> 添加任務

2> 構造

舉例:? 1> 添加任務 (簡單)?

使用 submit 把任務交給線程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Demo3 {public static void main(String[] args) {ExecutorService service = Executors.newFixedThreadPool(4);service.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}
}

?2> 構造方法 (重點)

構造方法中參數很多[經典面試題]

在 juc 包里面, 并發編程相關內容?

全部參數 如下圖:

對這 4 種情況 舉個例子:

我有 任務 A 要做, 朋友來讓我幫忙做任務 B, 這時我有 4 種回應方法.

1> 我心態崩了, 大哭. 拋出異常

2> 我對朋友說你自己做, 朋友自己做任務 B

3> 我的任務 A 不做了, 就去幫朋友

4> 我直接拒絕幫忙, 我仍然做任務 A , 朋友也不做任務 B 了

線程數目設置

使用線程池需要設置線程的數目, 設置多少合適?

具體數目是不對的, 需要實際情況分析

原因:

一個線程執行代碼主要有兩類:

1> cpu 密集型: 代碼主要是進行 算術運算/邏輯判斷

2> IO密集型: 代碼里主要進行的是 IO 操作

如果是 1>? 這個時候線程池的數量不要超過 N (設 N 就是極限), 比 N 更大, 就無法提高效率了, cpu吃滿了, 線程越多反而增加調度的開銷

如果是 2>? 不吃 CPU, 此時設置的線程數可以超過 N, 一個核心可以通過調度的方式來并發執行.

?實現線程池

class MyThreaPool {// 任務隊列private BlockingDeque<Runnable> queue = new ArrayBlockingQueue<>();// 通過這個方法, 把任務添加到隊列中public void submit(Runnable runnable) throws InterruptedException {//此處策略是第 5 種, 拒絕策略, 阻塞等待queue.offer(runnable);}public MyThreaPool(int n) {// 創建出 n 個線程, 負責執行上述隊列中的任務for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {// 讓這個線程從隊列中消費任務,并進行執行try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {e.printStackTrace();}});t.start();}}
}

?小結

認真學習各種多線程代碼實例, 理解其中的含義, 將各個代碼的的易錯點分析透徹

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/12845.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/12845.shtml
英文地址,請注明出處:http://en.pswp.cn/web/12845.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多年后,再探算法和數據結構

多年來&#xff0c;通過深入學習和實踐各種編程語言&#xff0c;我對數據結構和算法在程序設計中的中心地位有了新的認識。本次從匯編語言到高級編程語言的探討&#xff0c;展示了無論技術如何進步&#xff0c;構成程序的核心—算法和數據結構—始終保持其基礎和不變的角色。 …

圖解堆排序【一眼看穿邏輯思路】

P. S.&#xff1a;以下代碼均在VS2019環境下測試&#xff0c;不代表所有編譯器均可通過。 P. S.&#xff1a;測試代碼均未展示頭文件stdio.h的聲明&#xff0c;使用時請自行添加。 目錄 1、堆的概念2、實現堆排序前的準備工作3、堆排序的思路3.1 第一步3.2 第二步 4、結語 1、…

音視頻捕捉技術:LCC382 SDI采集卡深度解析

在日新月異的多媒體時代&#xff0c;高質量的音視頻采集已成為眾多領域不可或缺的一環。為此&#xff0c;靈卡科技精心打造了LCC382 —— 一款集高效性、靈活性與前沿技術于一身的SDI輸入與環出、HDMI輸出音視頻采集卡&#xff0c;旨在滿足從專業直播、視頻會議到醫療影像、安防…

網頁版Figma漢化

最近學習Figma&#xff0c;簡單介紹一下網頁版Figma的漢化方法 1.打開網址&#xff1a;Figma軟件漢化-Figma中文版下載-Figma中文社區 2.下載漢化插件離線包 解壓漢化包 3.點開谷歌的管理擴展程序 4.點擊加載已解壓的擴展程序&#xff0c;選擇剛剛解壓的包 這樣就安裝好了漢化…

QT狀態機2-含終止狀態的嵌套狀態機

#include "MainWindow.h" #include "ui_MainWindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)

前饋神經網絡FNN、多層感知機MLP和反向傳播推導

目錄 一、前饋神經網絡FNN 激活函數的使用 二、多層感知機MLP MLP的典型結構 多層感知機MLP的特點 和前饋神經網絡FNN的區別 三、傳播推導 1、前向傳播(Forward propagation) &#xff08;1&#xff09;輸入層到隱藏層 &#xff08;2&#xff09;隱藏層到輸出層 2、…

Java面試八股之WeakHashMap的工作原理

簡述WeakHashMap的工作原理 弱鍵&#xff08;Weak Keys&#xff09;&#xff1a; WeakHashMap 的鍵&#xff08;keys&#xff09;是通過 WeakReference 弱引用進行封裝的。弱引用是一種特殊的引用類型&#xff0c;它不會阻止所引用的對象被垃圾收集器回收。這意味著&#xff…

冥想訓練具體方法有哪些|流靜冥想

冥想是一種身體的放松和敏銳的警覺性相結合的狀態。 每日練習的好處遠不止你花在集中注意力的那幾分鐘。桑托雷利是建在烏斯特的馬薩諸塞大學醫學院的減壓診所的所長&#xff0c;她也是《自愈》的作者&#xff0c;她說&#xff1a;"冥想是一種工具&#xff0c;通過練習&a…

ubuntu無法遠程連接,ssh不可用,ssh遠程連接被拒絕的解決方法。啟動sshd遠程連接

1、用以下命令檢查ssh狀態 systemctl status sshd2、如果查不到sshd狀態&#xff0c;或提示沒有ssh&#xff0c;就安裝ssh服務器和客戶機 $ sudo apt install openssh-server # 安裝ssh服務器 $ sudo apt install openssh-client # 安裝ssh客戶機3、如果不能安裝openssh-…

構建安全的GenAI/LLMs核心技術解密之大模型對抗攻擊(一)

LlaMA 3 系列博客 基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (一) 基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (二) 基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (三) 基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (四) 基于 LlaMA…

云手機的優缺點分析

云手機&#xff0c;作為云計算領域的創新&#xff0c;致力于提供更為靈活的移動設備體驗&#xff0c;特別適用于那些希望在不同設備之間無縫切換的用戶。雖然云手機帶來了一系列優勢&#xff0c;但也伴隨著一些挑戰&#xff0c;比如網絡延遲可能會影響用戶體驗&#xff0c;特別…

網絡安全|隱藏IP地址的5種不同方法

隱藏計算機的IP地址在互聯網在線活動種可以保護個人隱私&#xff0c;這是在線活動的一種常見做法&#xff0c;包括隱私問題、安全性和訪問限制內容等場景。那么如何做到呢?有很5種方法分享。每種方法都有自己的優點和缺點。 1. 虛擬網絡 當您連接到虛擬服務器時&#xff0c;您…

openGauss學習筆記-284 openGauss AI特性-AI4DB數據庫自治運維-DBMind模式說明-component子命令

文章目錄 openGauss學習筆記-284 openGauss AI特性-AI4DB數據庫自治運維-DBMind模式說明-component子命令284.1 命令參考openGauss學習筆記-284 openGauss AI特性-AI4DB數據庫自治運維-DBMind模式說明-component子命令 該子命令可以用于啟動DBMind的組件,包括可用于監控指標的…

Dubbo配置上的一些概念

對于dubbo在spring中我們可能看到有如下配置&#xff08;可參考Schema 配置參考手冊 | Apache Dubbo&#xff09;&#xff1a; dubbo:application:id: dubbo-account-examplename: dubbo-account-example# 是否啟用 Dubbo 的 QoS&#xff08;Quality of Service&#xff09;服…

Blender雕刻建模_筆刷

1.雕刻模式 雕刻Scuplt&#xff0c;一種常用的建模方式 -選中物體&#xff0c;進入雕刻模式 -重構網格&#xff08;修改體素大小&#xff0c;點擊重構網格&#xff09;給物體添加更多面 -選擇筆刷&#xff0c;雕刻 -退出雕刻模式 2.重構網格 一種按體積的細分方式&#xf…

openstack部署nova中出現的問題:

[rootcontroller nova]# su -s /bin/sh -c “nova-manage db sync” nova /usr/lib/python2.7/site-packages/pymysql/cursors.py:170: Warning: (1831, u’Duplicate index block_device_mapping_instance_uuid_virtual_name_device_name_idx. This is deprecated and will be…

Springboot+MybatisPlus如何實現帶驗證碼的登錄功能

實現帶驗證碼的登錄功能由兩部分組成&#xff1a;&#xff1a;1、驗證碼的獲取 2、登錄&#xff08;進行用戶名、密碼和驗證碼的判斷&#xff09; 獲取驗證碼 獲取驗證碼需要使用HuTool中的CaptchaUtil.createLineCaptcha()來定義驗證碼的長度、寬度、驗證碼位數以及干擾線…

【JavaScript】嚴格模式

嚴格模式&#xff08;Strict Mode&#xff09;是一種運行模式&#xff0c;它提供了一種更加嚴格的語法和錯誤檢查&#xff0c;以幫助開發者編寫更可靠、更規范的代碼。 什么是嚴格模式&#xff1a; 嚴格模式是一種 JavaScript 的執行模式&#xff0c;通過啟用嚴格模式&#xff…

這個notebook集合,贊

這幾天在Github上看到一個數據科學倉庫&#xff0c;匯總了很多Python notebook代碼&#xff0c;主要是數據方向。 項目地址&#xff1a; https://github.com/donnemartin/data-science-ipython-notebooks 其中包括了pandas、numpy、matplotlib、scikit-learn、tensorflow、sp…

【Xilinx】程序可以綜合實現,但無法生成bit文件

項目場景&#xff1a; 使用xilinx vivado過程中遇到以下問題&#xff1a; 程序可以綜合實現&#xff0c;但無法生成bit文件 問題描述 最終生成bit文件時報錯如下 [DRC PDCN-1567] BUFGCTRL_CE_pins_both_connected_to_gnd: For cell ***/rxrecclk_bufg_i placed at site BU…