當面試官問出“Unsafe”類時,我就知道這場面試廢了,祖墳都能給你問出來!

一、寫在開頭

依稀記得多年以前的一場面試中,面試官從Java并發編程問到了鎖,從鎖問到了原子性,從原子性問到了Atomic類庫(對著JUC包進行了刨根問底),從Atomic問到了CAS算法,緊接著又有追問到了底層的Unsafe類,當問到Unsafe類時,我就知道這場面試廢了,這似乎把祖墳都能給問冒煙啊。

但時過境遷,現在再回想其那場面試,不再覺得面試官的追毛求疵,反而為那時候青澀菜雞的自己感到羞愧,為什么這樣說呢,實事求是的說Unsafe類雖然是比較底層,并且我們日常開發不可能用到的類,但是!翻開JUC包中的很多工具類,只要底層用到了CAS思想來提升并發性能的,幾乎都脫離不了Unsafe類的運用,可惜那時候光知道被八股文了,沒有做到細心總結與發現。

二、Unsafe的基本介紹

我們知道C語言可以通過指針去操作內存空間,Java不存在指針,為了提升Java運行效率、增強Java語言底層資源操作能力,便誕生了Unsafe類,Unsafe是位于sun.misc包下。正如它的名字一樣,這種操作底層的方式是不安全的,在程序中過度和不合理的使用,會帶來未知的風險,因此,Unsafe雖然,但要慎用哦!

2.1 如何創建一個unsafe實例

我們無法直接通過new的方式創建一個unsafe的實例,為什么呢?我們看它的這段源碼便知:

public final class Unsafe {// 單例對象private static final Unsafe theUnsafe;private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();// 僅在啟動類加載器`BootstrapClassLoader`加載時才合法if(!VM.isSystemDomainLoader(var0.getClassLoader())) {    throw new SecurityException("Unsafe");} else {return theUnsafe;}}
}

從源碼中我們發現Unsafe類被final修飾,所以無法被繼承,同時它的無參構造方法被private修飾,也無法通過new去直接實例化,不過在Unsafe 類提供了一個靜態方法getUnsafe,看上去貌似可以用它來獲取 Unsafe 實例。但是!當我們直接去調用這個方法的時候,會報如下錯誤:

Exception in thread "main" java.lang.SecurityException: Unsafeat sun.misc.Unsafe.getUnsafe(Unsafe.java:90)at com.cn.test.GetUnsafeTest.main(GetUnsafeTest.java:12)

這是因為在getUnsafe方法中,會對調用者的classLoader進行檢查,判斷當前類是否由Bootstrap classLoader加載,如果不是的話就會拋出一個SecurityException異常。

那我們如果想使用Unsafe類,到底怎樣才能獲取它的實例呢?

在這里提供給大家兩種方式:

方式一

假若在A類中調用Unsafe實例,則可通過Java命令行命令-Xbootclasspath/a把調用Unsafe相關方法的類A所在jar包路徑追加到默認的bootstrap路徑中,使得A被啟動類加載器加載,從而通過Unsafe.getUnsafe方法安全的獲取Unsafe實例。

java -Xbootclasspath/a: ${path}   // 其中path為調用Unsafe相關方法的類所在jar包路徑 

方式二

利用反射獲得 Unsafe 類中已經實例化完成的單例對象:

public static Unsafe getUnsafe() throws IllegalAccessException {Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");//Field unsafeField = Unsafe.class.getDeclaredFields()[0]; //也可以這樣,作用相同unsafeField.setAccessible(true);Unsafe unsafe =(Unsafe) unsafeField.get(null);return unsafe;}

2.2 Unsafe的使用

上面我們已經知道了如何獲取一個unsafe實例了,那現在就開始寫一個小demo來感受一下它的使用吧。

public class TestService {//通過單例獲取實例public static Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");//Field unsafeField = Unsafe.class.getDeclaredFields()[0]; //也可以這樣,作用相同unsafeField.setAccessible(true);Unsafe unsafe =(Unsafe) unsafeField.get(null);return unsafe;}//調用實例方法去賦值public void fieldTest(Unsafe unsafe) throws NoSuchFieldException {Persion persion = new Persion();persion.setAge(10);System.out.println("ofigin_age:" + persion.getAge());long fieldOffset = unsafe.objectFieldOffset(Persion.class.getDeclaredField("age"));System.out.println("offset:"+fieldOffset);unsafe.putInt(persion,fieldOffset,20);System.out.println("new_age:"+unsafe.getInt(persion,fieldOffset));}public static void main(String[] args) {TestService testService = new TestService();try {testService.fieldTest(getUnsafe());} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}
}
class Persion{private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

輸出:

ofigin_age:10
offset:12
new_age:20

通過 Unsafe 類的objectFieldOffset方法獲取到了對象中字段的偏移地址,這個偏移地址不是內存中的絕對地址而是一個相對地址,之后再通過這個偏移地址對int類型字段的屬性值進行讀寫操作,通過結果也可以看到 Unsafe 的方法和類中的get方法獲取到的值是相同的。

三、Unsafe類的8種應用

基于Unsafe所提供的API,我們大致可以將Unsafe根據應用場景分為如下的八類,上一個腦圖。
在這里插入圖片描述

3.1 內存操作

學習過C或者C++的同學對于內存操作應該很熟悉了,在Java里我們是無法直接對內存進行操作的,我們創建的對象幾乎都在堆內內存中存放,它的內存分配與管理都是JVM去實現,同時,在Java中還存在一個JVM管控之外的內存區域叫做“堆外內存”,Java中對堆外內存的操作,依賴于Unsafe提供的操作堆外內存的native方法啦。

內存操作的常用方法:

/*包含堆外內存的分配、拷貝、釋放、給定地址值操作*/
//分配內存, 相當于C++的malloc函數
public native long allocateMemory(long bytes);
//擴充內存
public native long reallocateMemory(long address, long bytes);
//釋放內存
public native void freeMemory(long address);
//在給定的內存塊中設置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//內存拷貝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//獲取給定地址值,忽略修飾限定符的訪問限制。與此類似操作還有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//為給定地址設置值,忽略修飾限定符的訪問限制,與此類似操作還有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//獲取給定地址的byte類型的值(當且僅當該內存地址為allocateMemory分配時,此方法結果為確定的)
public native byte getByte(long address);
//為給定地址設置byte類型的值(當且僅當該內存地址為allocateMemory分配時,此方法結果才是確定的)
public native void putByte(long address, byte x);

在這里我們不僅會想,為啥全是native方法呢?

  1. native方法通過JNI調用了其他語言,如果C++等提供的現車功能,可以讓Java拿來即用;
  2. 需要用到 Java 中不具備的依賴于操作系統的特性,Java 在實現跨平臺的同時要實現對底層的控制,需要借助其他語言發揮作用;
  3. 程序對時間敏感或對性能要求非常高時,有必要使用更加底層的語言,例如 C/C++甚至是匯編。

【經典應用】
在Netty、MINA等NIO框架中我們常常會應到緩沖池,而實現緩沖池的一個重要類就是DirectByteBuffer,它主要的作用對于堆外內存的創建、使用、銷毀等工作。

通常在I/O通信過程中,會存在堆內內存到堆外內存的數據拷貝操作,對于需要頻繁進行內存間數據拷貝且生命周期較短的暫存數據,都建議存儲到堆外內存

在這里插入圖片描述
在這里插入圖片描述
從上圖我們可以看到,在構建實例時,DirectByteBuffer內部通過Unsafe.allocateMemory分配內存、Unsafe.setMemory進行內存初始化,而后構建Cleaner對象用于跟蹤DirectByteBuffer對象的垃圾回收,以實現當DirectByteBuffer被垃圾回收時,分配的堆外內存一起被釋放。

3.2 內存屏障

為了充分利用緩存,提高程序的執行速度,編譯器在底層執行的時候,會進行指令重排序的優化操作,但這種優化,在有些時候會帶來 有序性 的問題。(在將volatile關鍵字的時候提到過了)

為了解決這一問題,Java中引入了內存屏障(Memory Barrier 又稱內存柵欄,是一個 CPU 指令),通過組織屏障兩邊的指令重排序從而避免編譯器和硬件的不正確優化情況。

在Unsafe類中提供了3個native方法來實現內存屏障:

//內存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//內存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//內存屏障,禁止load、store操作重排序
public native void fullFence();

【經典應用】
在之前的文章中,我們講過Java8中引入的一個高性能的讀寫鎖:StampedLock(鎖王),在這個鎖中同時支持悲觀讀與樂觀讀,悲觀讀就和ReentrantLock一致,樂觀讀中就使用到了unsafe的loadFence(),一起去看一下。

	/*** 使用樂觀讀鎖訪問共享資源* 注意:樂觀讀鎖在保證數據一致性上需要拷貝一份要操作的變量到方法棧,并且在操作數據時候					可能其他寫線程已經修改了數據,* 而我們操作的是方法棧里面的數據,也就是一個快照,所以最多返回的不是最新的數據,但是一致性還是得到保障的。** @return*/double distanceFromOrigin() {long stamp = sl.tryOptimisticRead(); // 獲取樂觀讀鎖double currentX = x, currentY = y;	// 拷貝共享資源到本地方法棧中if (!sl.validate(stamp)) { // //檢查樂觀讀鎖后是否有其他寫鎖發生,有則返回falsestamp = sl.readLock(); // 獲取一個悲觀讀鎖try {currentX = x;currentY = y;} finally {sl.unlockRead(stamp); // 釋放悲觀讀鎖}}return Math.sqrt(currentX * currentX + currentY * currentY);}

在官網給出的樂觀讀的使用案例中,我們看到if中做了一個根絕印章校驗寫鎖發生的操作,我們跟入這個校驗源碼中:

public boolean validate(long stamp) {U.loadFence();//load內存屏障return (stamp & SBITS) == (state & SBITS);}

這一步的目的是防止鎖狀態校驗運算發生重排序導致鎖狀態校驗不準確的問題!

3.3 對象操作

其實在2.2 Unsafe的使用中,我們已經使用了Unsafe進行對象成員屬性的內存偏移量獲取,以及字段屬性值的修改功能了,除了Int類型,Unsafe還支持對所有8種基本數據類型以及Object的內存數據修改,這里就不再贅述了。

需要額外強掉的一點,在Unsafe的源碼中還提供了一種非常規的方式進行對象的實例化:

//繞過構造方法、初始化代碼來創建對象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

這種方法可以繞過構造方法和初始化代碼塊來創建對象,我們寫一個小demo學習一下。

@Datapublic class A {private int b;public A(){this.b =1;}}

定義一個類A,我們分別采用無參構造器、newInstance()、Unsafe方法進行實例化。

public void objTest() throws Exception{A a1=new A();System.out.println(a1.getB());A a2 = A.class.newInstance();System.out.println(a2.getB());A a3= (A) unsafe.allocateInstance(A.class);System.out.println(a3.getB());}

輸出結果為1,1,0。這說明調用unsafe的allocateInstance方法確實可以跳過構造器去實例化對象!

3.4 數組操作

在 Unsafe 中,可以使用arrayBaseOffset方法獲取數組中第一個元素的偏移地址,使用arrayIndexScale方法可以獲取數組中元素間的偏移地址增量,通過這兩個方法可以定位數組中的每個元素在內存中的位置。

基于2.2 Unsafe使用的測試代碼,我們增加如下的方法:

  //獲取數組元素在內存中的偏移地址,以及偏移量private void arrayTest(Unsafe unsafe) {String[] array=new String[]{"aaa","bb","cc"};int baseOffset = unsafe.arrayBaseOffset(String[].class);System.out.println("數組第一個元素的偏移地址:" + baseOffset);int scale = unsafe.arrayIndexScale(String[].class);System.out.println("元素偏移量" + scale);for (int i = 0; i < array.length; i++) {int offset=baseOffset+scale*i;System.out.println(offset+" : "+unsafe.getObject(array,offset));}}

輸出:

數組第一個元素的偏移地址:16
元素偏移量4
16 : aaa
20 : bb
24 : cc

3.5 CAS相關

終于,重點來了,我們寫這篇文章的初衷是什么?是回想起曾經面時,面試官由原子類庫(Atomic)問到了CAS算法,從而追問到了Unsafe類上,在JUC包中到處都可以看到CAS的身影,在java.util.concurrent.atomic相關類、Java AQS、CurrentHashMap等等類中均有!

以AtomicInteger為例,在內部提供了一個方法為compareAndSet(int expect, int update) ,如果輸入的數值等于預期值,則以原子方式將該值設置為輸入值(update),而它的底層調用則是unsafe的compareAndSwapInt()方法。

public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

CAS思想的底層實現其實就是Unsafe類中的幾個native本地方法:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

3.6 線程調度

Unsafe 類中提供了park、unpark、monitorEnter、monitorExit、tryMonitorEnter方法進行線程調度,在前面介紹 AQS 的文章中我們學過,在AQS中通過調用LockSupport.park()和LockSupport.unpark()實現線程的阻塞和喚醒的,而LockSupport的park、unpark方法實際是調用Unsafe的park、unpark方式來實現。

//取消阻塞線程
public native void unpark(Object thread);
//阻塞線程
public native void park(boolean isAbsolute, long time);
//獲得對象鎖(可重入鎖)
@Deprecated
public native void monitorEnter(Object o);
//釋放對象鎖
@Deprecated
public native void monitorExit(Object o);
//嘗試獲取對象鎖
@Deprecated
public native boolean tryMonitorEnter(Object o);

LockSupport源碼:

public static void park(Object blocker) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);}public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}

3.7 Class操作

Unsafe 對Class的相關操作主要包括靜態字段內存定位、定義類、定義匿名類、檢驗&確保初始化等。

//獲取給定靜態字段的內存地址偏移量,這個值對于給定的字段是唯一且固定不變的
public native long staticFieldOffset(Field f);
//獲取一個靜態類中給定字段的對象指針
public native Object staticFieldBase(Field f);
//判斷是否需要初始化一個類,通常在獲取一個類的靜態屬性的時候(因為一個類如果沒初始化,它的靜態屬性也不會初始化)使用。 當且僅當ensureClassInitialized方法不生效時返回false。
public native boolean shouldBeInitialized(Class<?> c);
//檢測給定的類是否已經初始化。通常在獲取一個類的靜態屬性的時候(因為一個類如果沒初始化,它的靜態屬性也不會初始化)使用。
public native void ensureClassInitialized(Class<?> c);
//定義一個類,此方法會跳過JVM的所有安全檢查,默認情況下,ClassLoader(類加載器)和ProtectionDomain(保護域)實例來源于調用者
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
//定義一個匿名類
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

【測試案例】

@Datapublic class User {public static String name="javabuild";int age;}private void staticTest() throws Exception {User user=new User();//判斷是否需要初始化一個類,通常在獲取一個類的靜態屬性的時候(因為一個類如果沒初始化,它的靜態屬性也不會初始化)使用System.out.println(unsafe.shouldBeInitialized(User.class));Field sexField = User.class.getDeclaredField("name");//獲取給定靜態字段的內存地址偏移量long fieldOffset = unsafe.staticFieldOffset(sexField);//獲取一個靜態類中給定字段的對象指針Object fieldBase = unsafe.staticFieldBase(sexField);//根據某個字段對象指針和偏移量可以唯一定位這個字段。Object object = unsafe.getObject(fieldBase, fieldOffset);System.out.println(object);}

此外,在Java8中引入的Lambda表達式的實現中也使用到了defineClass和defineAnonymousClass方法。

3.8 系統信息

Unsafe 中提供的addressSize和pageSize方法用于獲取系統信息。

1) 調用addressSize方法會返回系統指針的大小,如果在 64 位系統下默認會返回 8,而 32 位系統則會返回 4。

2) 調用 pageSize 方法會返回內存頁的大小,值為 2 的整數冪。

使用下面的代碼可以直接進行打印:

private void systemTest() {System.out.println(unsafe.addressSize());System.out.println(unsafe.pageSize());
}

輸出為:8,4096

四、總結

哎呀,媽呀,終于寫完了,人要傻了,為了整理這篇文章看了大量的源碼,人看的頭大,跟俄羅斯套娃似的源碼,嚴謹的串聯在一起!Unsafe類在日常的面試中確實不經常被問到,大家稍微了解一下即可。

五、結尾彩蛋

如果本篇博客對您有一定的幫助,大家記得留言+點贊+收藏呀。原創不易,轉載請聯系Build哥!
在這里插入圖片描述
如果您想與Build哥的關系更近一步,還可以關注“JavaBuild888”,在這里除了看到《Java成長計劃》系列博文,還有提升工作效率的小筆記、讀書心得、大廠面經、人生感悟等等,歡迎您的加入!
在這里插入圖片描述

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

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

相關文章

【c語言】函數遞歸

在C語言中&#xff0c;函數遞歸是一種函數調用自身的技術。遞歸函數通常用于解決可以分解為更小、類似子問題的問題。遞歸函數有兩個基本部分&#xff1a; 基本情況&#xff08;Base Case&#xff09;&#xff1a;這是遞歸的終止條件&#xff0c;即函數停止遞歸并返回值的條件…

Mysql with 用法

什么是 with 語句 WITH 子句是 MySQL 中的一種 SQL 結構&#xff0c;又稱為 Common Table Expression (CTE)。它在不影響原有 SQL 語句的情況下&#xff0c;允許開發人員臨時創建一個內存中的結果集&#xff0c;然后對其進行操作。 with 語句用法 語法格式 WITH cte_name (…

JVM-調優之-高cpu線程問題排查

這里主要是對jstack命令的使用&#xff1b; 程序在運行過程中卡主&#xff0c;前端無法訪問&#xff0c;一看服務器CPU占用達到200到300%多。 排查思路 1&#xff09;找出占用高的進程 2&#xff09;找出占用高的線程 3&#xff09;找出具體的代碼 分析步驟&#xff1a; …

C++里的vector詳細講解

在C的標準模板庫&#xff08;STL&#xff09;中&#xff0c;vector是一個非常有用的動態數組容器。它允許我們存儲可變大小的同類型元素序列&#xff0c;并且能夠動態地增長和縮小。由于其靈活性和易用性&#xff0c;vector在C編程中得到了廣泛的應用。 一、vector的基本操作 …

Java異常處理:最佳實踐與常見模式

在Java編程中&#xff0c;異常處理是保證程序健壁性和穩定性的重要方面。良好的異常處理不僅可以幫助程序在面對錯誤情況時恢復到正常狀態&#xff0c;還可以提供錯誤診斷的信息&#xff0c;輔助開發者快速定位問題。本文將探討Java中的異常處理機制&#xff0c;包括異常的分類…

esp32 Micropython 長按按鍵動作一次代碼

1. 長按按鍵&#xff0c;松手后動作 from machine import Pin import timeEnter_key Pin(15, Pin.IN, Pin.PULL_UP) Enter_key_flag 0 Enter_key_flag_temp 0while True:if Enter_key.value() 0:time.sleep_ms(10)while Enter_key.value() 0:Enter_key_flag_temp not En…

System.Collections.Generic 中的接口和類型區分

System.Collections.Generic 命名空間包含了許多與泛型集合相關的接口和類。這些接口定義了一組通用的集合行為&#xff0c;而具體的實現&#xff08;如 List、Dictionary<TKey, TValue> 等&#xff09;則遵循這些接口&#xff0c;從而提供具體的集合功能。以下是 System…

前后端開發入門全攻略:零基礎學起

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、前后端開發概覽 二、后端開發基礎&#xff1a;Flask框架入門 代碼案例&#xff1a;Hel…

vue3之使用圖片實現類似于 el-radio 的單選框功能,并且可實現選中和取消選中

背景 我們在工作中常用的一般都是使用類似于 element-plus 中的 el-radio 或者是 el-checkbox 來實現單選或者多選 若有一天我們遇到了一個新的業務需求,需要使用 圖片 來實現類似于 el-radio 的功能,并且要求實現第一次點擊時處于選中狀態,當我們再次點擊時處于非選中狀態…

談戀愛沒經驗?那就來刷談戀愛經驗寶寶吧

??作者主頁&#xff1a;小虛竹 ??作者簡介&#xff1a;大家好,我是小虛竹。2022年度博客之星評選TOP 10&#x1f3c6;&#xff0c;Java領域優質創作者&#x1f3c6;&#xff0c;CSDN博客專家&#x1f3c6;&#xff0c;華為云享專家&#x1f3c6;&#xff0c;掘金年度人氣作…

自動駕駛---Tesla的自動駕駛技術進化史(PerceptionPlanning)

1 前言 筆者在專欄《自動駕駛Planning模塊》中已經詳細講解了傳統自動駕駛Planning模塊的內容&#xff1a;包括行車的Behavior Planning和Motion Planning&#xff0c;以及低速記憶泊車的Planning&#xff08;最開始有15篇&#xff0c;目前逐漸更新到17篇&#xff09;。讀者對整…

【Spring】SSM介紹_SSM整合

1、SSM介紹 1.1簡介 SSM&#xff08;Spring SpringMVC MyBatis&#xff09;整合是一種流行的Java Web應用程序框架組合&#xff0c;它將Spring框架的核心特性、SpringMVC作為Web層框架和MyBatis作為數據訪問層框架結合在一起。這種整合方式提供了從數據訪問到業務邏輯處理再…

5.18 TCP機械臂模擬

#include <netinet/tcp.h>//包含TCP選項的頭文件 #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/input.h>//讀取輸入事件 #include <sys/types.h> #include <sys/stat.h&…

對于mybatis和mybatisplus的選擇

對于mybatis和mybatisplus的選擇 1. 問題2. MP單表操作2.1 單表普通查詢2.2 單表分頁查詢 3. mybatis多表操作3.1 多表普通查詢3.2 多表分頁查詢 1. 問題 mybatis 和 mybatisplus作為當下主流的持久層框架&#xff0c;各有優劣勢。依據個人經驗&#xff1a;mybatis可以定制化輸…

一文詳解邏輯越權漏洞

1. 邏輯越權 1.1. 漏洞原理 邏輯越權漏洞就是當用戶跳過自己的權限限制&#xff0c;去操作同等級用戶或者上級用戶。正常的情況下&#xff0c;當一個用戶去訪問某個資源的時候&#xff0c;首先需要去登錄驗證自己的權限&#xff0c;其次是對數據的查詢&#xff0c;最后返回數…

gateway基本配置,如何配置?

文章推薦 1 作為程序員&#xff0c;開發用過最好用的AI工具有哪些&#xff1f; 2 Github Copilot正版的激活成功&#xff0c;終于可以chat了 3 idea,pycharm等的ai assistant已成功激活 4 新手如何拿捏 Github Copilot AI助手&#xff0c;幫助你提高寫代碼效率 5 Jetbrains的a…

linux命令中arpd的使用

arpd 收集免費ARP信息 補充說明 arpd命令 是用來收集免費arp信息的一個守護進程&#xff0c;它將收集到的信息保存在磁盤上或者在需要時&#xff0c;提供給內核用戶用于避免多余廣播。 語法 arpd(選項)(參數)選項 -l&#xff1a;將arp數據庫輸出到標準輸出設備顯示并退出…

【云原生】Kubernetes----POD基本管理

目錄 引言 一、Pod基礎概念 &#xff08;一&#xff09;Pod簡介 &#xff08;二&#xff09;Pod的分類 1.自主式Pod 2.控制器管理的Pod &#xff08;三&#xff09;Pod使用方式 1.單容器pod 2.多容器Pod 3. 注意事項 二、Pod容器的分類 &#xff08;一&#xff09;…

C#中的惰性對象你使用過嗎?

概述&#xff1a;本文深入探討了 C# 中 Lazy Objects 的概念。惰性對象是高效資源配置和初始化的非常有益的工具&#xff0c;尤其是在這些過程成本高昂或耗時的情況下。它全面研究了 Lazy Objects 的功能、其目的和最佳用例&#xff0c;以及實際的實現策略。延遲加載簡介什么是…

【Unity】免費的高亮插件——QuickOutline

除了常見的HighLightSystem來實現的高亮功能&#xff0c;其實還有很多的方法實現物體的高亮。 在 Unity資源商店 搜索OutLine&#xff0c;就會有很多免費好用的高亮插件。 下面介紹一下 QuickOutline這個插件&#xff0c;在 Unity資源商店 搜索到后&#xff0c;點擊進去就可以…