posted @ 2024-10-31 11:27 為自己代言 閱讀(17) | 評論 (0) | 編輯 收藏
其中dataid 是指配置文件名稱;group:相當于給配置文件分類;namespace:用來隔離環境的(例如:dev,test,product)
比較關注的點是 bootstrap.yml; application.yml 這兩文件用途和加載順序
- bootstrap.yml 用于配置nacos地址,用戶名,密碼,命名空間(相當要先連接上nacos),然后才能拉去nacos上的配置文件信息;
- application.yml 文件用于配置在本地的配置文件(其實這個也可以放到nacos上)
bootstrap配置文件是spring cloud新增的啟動配置文件,需要引入spring-cloud-context依賴后,才會進行加載(看spring cloud 2022版本以以前)。
bootstrap由父ApplicationContext加載,所以比application優先加載
因為bootstrap優先于application加載,所以不會被覆蓋
使用配置中心spring cloud config時,需要在bootstarp中配置配置中心的地址,從而實現父ApplicationContext加載時,從配置中心拉去相應的配置到應用中。
在springboot工程中使用基礎上使用兩個注釋:@RefreshScope 放在類上和@value 配置使用要實現取值和動態刷新通過實驗證明使用@NacosValue是不行了,因為使用了Spring Cloud的依賴包,所以根據官方文檔顯示,要通過 Spring Cloud 原生注解 @RefreshScope + @Value 來實現配置自動更新,我們可以用下面的方法達到同樣的效。
nacos client 從服務端拉取配置信息會放到client 本地緩存起來 默認目錄:${user}\nacos\config 下(拉到調試時候有用)詳細文章 :https://developer.aliyun.com/article/859891
posted @ 2024-10-27 22:16 為自己代言 閱讀(32) | 評論 (0) | 編輯 收藏
posted @ 2021-09-26 18:09 為自己代言| 編輯 收藏
InnoDB的鎖
InnoDB的行鎖:共享鎖、排他鎖、MDL鎖
共享鎖:又稱讀鎖、S鎖。一個事務獲取一個數據行的共享鎖,其他事務能獲取該行對應的共享鎖,但不能獲得排他鎖;即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對數據進行增刪改查。
應用:
1.自動提交模式下的select查詢,不加任何鎖,直接返回查詢結果
2.通過select……lock in share mode在被讀取的行記錄或范圍上加一個讀鎖,其他事務可以讀,但是申請加寫鎖會被阻塞
排他鎖:又稱寫鎖、X鎖。一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖,寫鎖優先級最高。
應用:
1.一些DML操作會對行記錄加寫鎖
2.select for update會對讀取的行記錄上加一個寫鎖,其他任何事務都不能對鎖定的行加任何鎖,否則會被阻塞
MDL鎖:MySQL5.5引入,用于保證表中元數據的信息。在會話A中,表開啟了查詢事務后,會自動獲得一個MDL鎖,會話B就不能執行任何DDL語句的操作
行鎖實現方式
InnoDB 行鎖是通過給索引上的索引項加鎖來實現的,這一點 MySQL 與 Oracle 不同,后者是 通過在數據塊中對相應數據行加鎖來實現的。InnoDB 這種行鎖實現特點意味著:只有通過 索引條件檢索數據,InnoDB 才使用行級鎖,否則,InnoDB 將使用表鎖! 在實際應用中,要特別注意 InnoDB 行鎖的這一特性,不然的話,可能導致大量的鎖沖突, 從而影響并發性能。
行鎖的三種算法
InnoDB 存儲引擎有三種行鎖的算法,其分別是:
- Record Lock: 單個行記錄上的鎖
- Gap Lock: 間隙鎖,鎖定一個范圍,但不包含記錄本身
- Next-Key 鎖: Gap Lock + Record Lock,鎖定一個范圍,并且會鎖定記錄本身
RC模式下只采用Record Lock,RR模式下采用了Next-Key
加鎖場景分析
- 主鍵索引
如果我們加鎖的行上存在主鍵索引,那么就會在這個主鍵索引上添加一個 Record Lock。
- 輔助索引
如果我們加鎖的行上存在輔助索引,那么我們就會在這行的輔助索引上添加 Next-Key Lock,并在這行之后的輔助索引上添加一個 Gap Lock
輔助索引上的 Next-Key Lock 和 Gap Lock 都是針對 Repeatable Read 隔離模式存在的,這兩種鎖都是為了防止幻讀現象的發生。
- 唯一的輔助索引
這里有一個特殊情況,如果輔助索引是唯一索引的話,MySQL 會將 Next-Key Lock 降級為 Record Lock,只會鎖定當前記錄的輔助索引。
如果唯一索引由多個列組成的,而我們只鎖定其中一個列的話,那么此時并不會進行鎖降級,還會添加 Next-Key Lock 和 Gap Lock。
- Insert 語句
在 InnoDB 存儲引擎中,對于 Insert 的操作,其會檢查插入記錄的下一條記錄是否被鎖定,若已經被鎖定,則不允許查詢。
意向鎖
意向鎖可以分為意向共享鎖(Intention Shared Lock, IS)和意向排他鎖(Intention eXclusive
Lock,
IX)。但它的鎖定方式和共享鎖和排他鎖并不相同,意向鎖上鎖只是表示一種“意向”,并不會真的將對象鎖住,讓其他事物無法修改或訪問。例如事物T1想要修改表test
中的行r1
,它會上兩個鎖:
- 在表
test
上意向排他鎖 - 在行
r1
上排他鎖
事物T1在test
表上上了意向排他鎖,并不代表其他事物無法訪問test
了,它上的鎖只是表明一種意向,它將會在db
中的test
表中的某幾行記錄上上一個排他鎖。
意向共享鎖 | 意向排他鎖 | 共享鎖 | 排他鎖 | |
---|---|---|---|---|
意向共享鎖 | 兼容 | 兼容 | 兼容 | 不兼容 |
意向排他鎖 | 兼容 | 兼容 | 不兼容 | 不兼容 |
共享鎖 | 兼容 | 不兼容 | 兼容 | 不兼容 |
排他鎖 | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
一致性非鎖定讀
一致性非鎖定讀是指 InnoDB 存儲引擎通過行多版本控制(multi version)的方式來讀取當前執行時間數據庫中行的數據。具體來說就是如果一個事務讀取的行正在被鎖定,那么它就會去讀取這行數據之前的快照數據,而不會等待這行數據上的鎖釋放。這個讀取流程如圖1所示:
行的快照數據是通過undo段來實現的,而undo段用來回滾事務,所以快照數據本身沒有額外的開銷。此外,讀取快照數據時不需要上鎖的,因為沒有事務會對快照數據進行更改。
MySQL 中并不是每種隔離級別都采用非一致性非鎖定讀的讀取模式,而且就算是采用了一致性非鎖定讀,不同隔離級別的表現也不相同。在 READ COMMITTED 和 REPEATABLE READ 這兩種隔離級別下,InnoDB存儲引擎都使用一致性非鎖定讀。但是對于快照數據,READ COMMITTED 隔離模式中的事務讀取的是當前行最新的快照數據,而 REPEATABLE READ 隔離模式中的事務讀取的是事務開始時的行數據版本。
一致性鎖定讀
在 InnoDB 存儲引擎中,select
語句默認采取的是一致性非鎖定讀的情況,但是有時候我們也有需求需要對某一行記錄進行鎖定再來讀取,這就是一致性鎖定讀。
InnoDB 對于select
語句支持以下兩種鎖定讀:
select ... for update
select ... lock in share mode
select ... for update
會對讀取的記錄加一個X鎖,其他事務不能夠再來為這些記錄加鎖。select ... lock in share mode
會對讀取的記錄加一個S鎖,其它事務能夠再為這些記錄加一個S鎖,但不能加X鎖。
對于一致性非鎖定讀,即使行記錄上加了X鎖,它也是能夠讀取的,因為它讀取的是行記錄的快照數據,并沒有讀取行記錄本身。
select ... for update
和select ... lock in share mode
這兩個語句必須在一個事務中,當事務提交了,鎖也就釋放了。因此在使用這兩條語句之前必須先執行begin
, start transaction
,或者執行set autocommit = 0
。
InnoDB 在不同隔離級別下的一致性讀及鎖的差異
consisten read //一致性讀
share locks //共享鎖
Exclusive locks //排他鎖
讀未提交 | 讀已提交 | 可重復讀 | 串行化 | ||
---|---|---|---|---|---|
SQL | 條件 | ||||
select | 相等 | None locks | Consisten read/None lock | Consisten read/None lock | Share locks |
范圍 | None locks | Consisten read/None lock | Consisten read/None lock | Share Next-Key | |
update | 相等 | Exclusive locks | Exclusive locks | Exclusive locks | Exclusive locks |
范圍 | Exclusive next-key | Exclusive next-key | Exclusive next-key | Exclusive next-key | |
Insert | N/A | Exclusive locks | Exclusive locks | Exclusive locks | Exclusive locks |
Replace | 無鍵沖突 | Exclusive locks | Exclusive locks | Exclusive locks | Exclusive locks |
鍵沖突 | Exclusive next-key | Exclusive next-key | Exclusive next-key | Exclusive next-key | |
delete | 相等 | Exclusive locks | Exclusive locks | Exclusive locks | Exclusive locks |
范圍 | Exclusive next-key | Exclusive next-key | Exclusive next-key | Exclusive next-key | |
Select … from … Lock in share mode | 相等 | Share locks | Share locks | Share locks | Share locks |
范圍 | Share locks | Share locks | Exclusive next-key | Exclusive next-key | |
Select * from … For update | 相等 | Exclusive locks | Exclusive locks | Exclusive locks | Exclusive locks |
范圍 | Exclusive locks | Exclusive locks | Exclusive next-key | Exclusive next-key | |
Insert into … Select … | innodb_locks_ unsafe_for_bi nlog=off | Share Next-Key | Share Next-Key | Share Next-Key | Share Next-Key |
(指源表鎖) | innodb_locks_ unsafe_for_bi nlog=on | None locks | Consisten read/None lock | Consisten read/None lock | Share Next-Key |
create table … Select … | innodb_locks_ unsafe_for_bi nlog=off | Share Next-Key | Share Next-Key | Share Next-Key | Share Next-Key |
(指源表鎖) | innodb_locks_ unsafe_for_bi nlog=on | None locks | Consisten read/None lock | Consisten read/None lock | Share Next-Key |
在了解 InnoDB 鎖特性后,用戶可以通過設計和 SQL 調整等措施減少鎖沖突和死鎖,包括:
- 盡量使用較低的隔離級別;
- 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
- 選擇合理的事務大小, 小事務發生鎖沖突的幾率也更小;
- 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
- 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
- 盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響;
- 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
- 對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。
參考資料
1.https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-intention-locks mysql官網開發手冊
2.《MySQL 技術內幕 – InnoDB 存儲引擎》
3.《深入淺出MySQL》
4.https://www.modb.pro/db/33873
posted @ 2021-07-12 15:38 為自己代言 閱讀(113) | 評論 (0) | 編輯 收藏
整體架構

EagleEye 的核心
- TraceId

如上圖所示,應用A是接受到來自用戶瀏覽器的Web請求的前端服務器,它是一條調用鏈的開始端,在TBSession和EagleEyeFilter中都做了EagleEye上下文埋點。請求收到后它會先調用EagleEye StartTrace生成TraceId并放置在當前線程的ThreadLocal,日志埋點請求信息(如URL、SessionId、UserId等)。在請求處理完畢提交相應時,再調用EndTrace清理線程中的EagleEye信息。 在應用A調用應用B、C的HSF服務,或者發送Notify消息時,TraceId被包含在EagleEye上下文中,隨網絡請求到達應用B、C、D、E之中,并放置在線程ThreadLocal內,因此后續調用到的這些系統都會有EagleEye這次請求的上下文。這些系統再發起網絡請求時,也類似的攜帶了上下文信息的。
- RpcId
為了區別同一個調用鏈下多個網絡調用的順序和嵌套層次,EagleEye還需要傳輸和記錄RpcId。 RpcId用0.X1.X2.X3.....Xi來表示,Xi都是非負整數,根節點的RpcId固定從0開始,第一層網絡調用的RpcId是0.X1,第二層的則為0.X1.X2,依次類推。*例如,從根節點發出的調用的RpcId是0.1、0.2、0.3,RpcId是0.1的節點發出的RpcId則為0.1.1、0.1.2、0.1.3。如下圖所示
通過RpcId,可以準確的還原出調用鏈上每次調用的層次關系和兄弟調用之間的先后順序。 例如上圖應用 G 的兩次調用0.2.1.1和0.1.2.1,可以看出對 DB 的訪問0.2.1.1源于 C 到 G 的調用0.2.1,對 Tair 的訪問0.1.2.1源于B 到 G 的調用0.1.2。 很多調用場景會比上面說的完全同步的調用更為復雜,比如會遇到異步、單向、廣播、并發、批處理等等,這時候需要妥善處理好ThreadLocal上的調用上下文,避免調用上下文混亂和無法正確釋放。另外,采用多級序號的RpcId設計方案會比單級序號遞增更容易準確還原當時的調用情況。
posted @ 2021-06-04 15:36 為自己代言 閱讀(576) | 評論 (0) | 編輯 收藏
- 基于數據庫實現
- 基于redis實現(這個對于不太敏感的場景可以使用,由于redis集群和單機,還有客戶端,版本等多方面因素考慮情況比較多)
- 基于zookeeper實現(這個是最終也是最好最可靠的)
從理解的難易程度角度(從低到高)下面講基于redis實現分布鎖代碼:RedisTemplate 客戶端 lettuce
數據庫 > 緩存 > Zookeeper
從實現的復雜性角度(從低到高)
Zookeeper > 緩存 > 數據庫
從性能角度(從高到低)
緩存 > Zookeeper >= 數據庫
從可靠性角度(從高到低)
Zookeeper > 緩存 > 數據庫
public class RedisDistributedLockUtils {
@Autowired
private RedisTemplate redisTemplate;
private static final Long RELEASE_SUCCESS = 1L;
private static final long DEFAULT_TIMEOUT = 1000 * 10;
//因為要使用lua 腳本是因為 redis 執行lua腳本是原子操作
private static final String UNLOCK_LUA= "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* 實時獲取鎖
*
* 嘗試獲取分布式鎖 將redis版本升級到2.1以上(spring-boot-starter-data-redis 版本 2.X以上),然后使用setIfAbsent 不存在
* 當setIfAbsent成功之后斷開連接,下面設置過期時間的代碼 stringRedisTemplate.expire(key,timeout);是無法執行的,這時候就會有大量沒有過期時間的數據存在數據庫
* @param lockKey 鎖
* @param requestId 請求標識
* @param expireTime 超期時間
* @return 是否獲取成功
*/
public boolean trySetDistributedLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId,0 == expireTime ? DEFAULT_TIMEOUT : expireTime, TimeUnit.MILLISECONDS);
}
/**
* 以阻塞方式的獲取鎖
* @param key
* @param value
* @param timeout
* @return
*/
public boolean setDistributedLock(String key, String value, long timeout) {
Boolean lock = false;
long start = System.currentTimeMillis();
while (!lock && (System.currentTimeMillis() - start < timeout)) {
//執行set命令
lock = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);
//不頻繁去獲取鎖
try {
if (!lock) {
Thread.sleep(60);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return lock;
}
public boolean releaseLock(String key, String value) {
// 使用Lua腳本:先判斷是否是自己設置的鎖,再執行刪除
// 使用lua腳本刪除redis中匹配value的key,可以避免由于方法執行時間過長而redis鎖自動過期失效的時候誤刪其他線程的鎖
// spring自帶的執行腳本方法中,集群模式直接拋出不支持執行腳本的異常EvalSha is not supported in cluster environment
// 所以只能拿到原redis的connection來執行腳本
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> args = new ArrayList<>();
args.add(value);
Long result = (Long)redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
Object nativeConnection = connection.getNativeConnection();
// 集群模式和單機模式雖然執行腳本的方法一樣,但是沒有共同的接口,所以只能分開執行
// 集群模式
if (nativeConnection instanceof JedisCluster) {
return (Long)((JedisCluster)nativeConnection).eval(UNLOCK_LUA, keys, args);
}
//客戶端是Jedis時候(單機模式)
else if (nativeConnection instanceof Jedis) {
return (Long)((Jedis)nativeConnection).eval(UNLOCK_LUA, keys, args);
}
//這里使用 redisTemplate 中lettuce 客戶端
else{
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(UNLOCK_LUA);
redisScript.setResultType(Long.class);
return (Long)redisTemplate.execute(redisScript, keys, value);
}
}
});
//返回最終結果
return RELEASE_SUCCESS.equals(result);
}
}
基于zookeeper實現下期補上:
介紹分布式鎖文章寫的比較詳細:
https://blog.csdn.net/u010963948/article/details/79006572
posted @ 2021-03-24 20:11 為自己代言 閱讀(204) | 評論 (0) | 編輯 收藏
1、三種方法
- 唯一的抽象方法
- 使用default定義普通方法(默認方法),通過對象調用。
- 使用static定義靜態方法,通過接口名調用。
2、一個新注解@FunctionInterface
- 注解@FunctionalInterface告訴編譯器這是一個函數式接口,明確這個函數中只有一個抽象方法,當你嘗試在接口中編寫多個抽象方法的時候編譯器將不允許,但是可以有多個非抽象方法。
- 不過Object類的方法可以定義為抽象方法,因為接口的實現類一定是Object的子類
- 如果接口被標注了@FunctionalInterface,這個類就必須符合函數式接口的規范
- 即使一個接口沒有標注@FunctionalInterface,如果這個接口滿足函數式接口規則,依舊被當作函數式接口。
3、JDK 1.8 新增加的函數接口包:
java.util.function.*
java.util.function 它包含了很多接口,用來支持 Java的 函數式編程,它們大致分為五類:
*JDK 8 函數式接口 Supplier、Function、Consumer、Predicate
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ThreadPoolExecutor executor = (ThreadPoolExecutor)newFixedThreadPool(10);
//1:JDK8以前,通過匿名內部類實現函數式接口
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("JDK8以前,通過匿名內部類實現函數式接口");
}
});
//2:JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生(分開寫效果)
Thread thread = new Thread(() -> System.out.println("task running !"));
Runnable r = () -> System.out.println("JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生(分開寫效果)!");
executor.submit(r);
//3:合并起來效果
executor.submit(() -> {
System.out.println("JDK8以后可以使用lambda 表達式來實現,lambda表達式就是為了優化匿名內部類而生!");
});
//4:其它 Supplier、Function、Consumer、Predicate 都可以用lambda 表達式來實現
Supplier<String> supplier = () -> "我是SuSupplier";
Supplier<Integer> supplier2 = () -> new Integer(1);
System.out.println("supplier=" + supplier.get() + ";supplier2=" + supplier2.get());
//5: Function功能型函數式接口 Function<T, R> 接受一個輸入參數T,返回一個結果R
Function<String,Integer> function=str -> Integer.parseInt(str);
Function<Integer,String> fun2 = item -> item+"";
System.out.println("輸入字符型 1 返回int型結果:"+function.apply("1"));
System.out.println("輸入整型 1 返回字符型結果:"+fun2.apply(2));
//6: Consumer 一個接受單個輸入參數并且不返回結果的操作。 與大多數其他函數接口不同, Consumer接口期望通過接受參數,改普通對象引用值(說明白點就是對原來的值進行加工,注意返回值 void)
Consumer<StringBuffer> consumer= sb->sb.append("-yyy");
StringBuffer sb1=new StringBuffer().append("111");
consumer.accept(sb1);
//改變sb的內部引用值
System.out.println("=========s="+sb1.toString());
//7: Predicate<T> 斷言型接口常用于集合的過濾,得到一個新的集合 Stream filter(Predicate<? super T> predicate);
Predicate<Integer> predicate = age -> age > 18;
Predicate<String> predicate2 = str -> str != null;
System.out.println(predicate.test(19));
System.out.println(predicate2.test(null));
//我們常用集合過濾類就是對這個接口實現類 其中 filter(Predicate<? super T> predicate) 用的就是這個接口
List<String> list= Lists.newArrayList("1","2","2","3","4","4","8");
list.stream().map(s -> Long.parseLong(s)).distinct().filter(s -> s < 10).collect(Collectors.toList()).forEach(u -> System.out.println(u));
//總結,以上的例子其實都是JDK8 lambda 表達式簡潔的寫法,而且全是合并寫的,并沒有分開步驟寫(所有函數性接口,都可以用lambda 表達式簡潔寫法)
//關閉線程池
executor.shutdownNow();
}
posted @ 2020-07-24 15:46 為自己代言 閱讀(1328) | 評論 (0) | 編輯 收藏
JDK 8 中 CompletableFuture 是對 Future 的增強 大大簡化了異步編程步驟,在Spring 框架中配合@EnableAsync @Async 更加事辦功倍。
1:在JDK 8 之前實現多線必需實現兩個接口 Runnable 不帶返回值,另一個Callable帶返回值的接口,配合ThreadPoolTaskExecutor.submit(Callable callable) 返回一個Future對象。 使用Future獲得異步執行結果時,要么調用阻塞方法get(),要么輪詢看isDone()是否為true,這兩種方法都不是很好,因為主線程也會被迫等待,而CompletableFuture出現改變了這個問題,而且提供更多并且強大的其它功能。
2:CompletableFuture簡介 CompletableFuture<T> implements Future<T>, CompletionStage<T> 其實CompletableFuture 除了實現原來的Future 接口外,其它大部分方法都是在CompletionStage中
大致介紹下completableFuture的命名規則:
1:按功能分類的話:
xxx()
:表示該方法將繼續在已有的線程中執行;xxxAsync()
:表示將異步在線程池中執行。- 異步執行方法默認一個參數的話任務是在
ForkJoinPool.commonPool()
線程池中執行的,帶executor 參數的使用 executor線程池異步執行。
2:按邏輯和組織方式來分話(completableFuture 中大約有50個來方法)
- 一種是 then 的邏輯,即前一個計算完成的時候調度后一個計算
- 一種是 both 的邏輯,即等待兩個計算都完成之后執行下一個計算,只要能組合一個和另一個,我們就可以無限復用這個 +1 的邏輯組合任意多的計算
- 另一種是 either 的邏輯,即等待兩個計算的其中一個完成之后執行下一個計算。注意這樣的計算可以說是非確定性的。因為被組合的兩個計算中先觸發下一個計算執行的那個會被作為前一個計算,而這兩個前置的計算到底哪一個先完成是不可預知的
3:從依賴關系和出入參數類型區別,基本分為三類:
- apply 字樣的方式意味著組合方式是
Function
,即接受前一個計算的結果,應用函數之后返回一個新的結果 - accept 字樣的方式意味著組合方式是
Consumer
,即接受前一個計算的結果,執行消費后不返回有意義的值 - run 字樣的方式意味著組合方式是
Runnable
,即忽略前一個計算的結果,僅等待它完成后執行動作
http://www.aygfsteel.com/zzzlyr/articles/435611.html
4:completableFuture 配合框架使用
因為自從JDK8以后增強了多線程的使用便捷程度:http://www.aygfsteel.com/zzzlyr/articles/435305.html
1:JDk8 的函數式接口和lambda表過式
2:completableFuture 對 Future 類的增強。
這只是JDK 基礎包中的功能,現在大部分開發都在使用框架 java 現在基本上都在使用spring框架,因為JDK基礎包中的功能還是不如框架使用方便,下邊文章詳細介紹 springboot中對JDK基礎包中多線程功能配置和使用。
posted @ 2020-07-23 19:29 為自己代言 閱讀(858) | 評論 (0) | 編輯 收藏
posted @ 2020-03-25 20:19 為自己代言 閱讀(8105) | 評論 (0) | 編輯 收藏
如上圖找出最耗CPU 進程 10765
統計信息區
前五行是系統整體的統計信息。第一行是任務隊列信息,同 uptime 命令的執行結果。其內容如下:
01:06:48 | 當前時間 |
up 1:22 | 系統運行時間,格式為時:分 |
1 user | 當前登錄用戶數 |
load average: 0.06, 0.60, 0.48 | 系統負載,即任務隊列的平均長度。 三個數值分別為 1分鐘、5分鐘、15分鐘前到現在的平均值。 |
步驟二:找出最耗CPU的線程
- top -Hp 10765 顯示一個進程的線程運行信息列表
- 鍵入shift +p 線程按照CPU使用率降序排序
命令: printf '%x' 10765 輸出結果:2a0d
步驟四: 使用JVM命令 jstatck
jstack 10765 | grep '2a0d' -C5 --color 打印堆棧信息,通過id 過濾到線程的堆棧信息。
以下是top 其它常用命令:
附常用操作:
top //每隔5秒顯式所有進程的資源占用情況
top -d 2 //每隔2秒顯式所有進程的資源占用情況
top -c //每隔5秒顯式進程的資源占用情況,并顯示進程的命令行參數(默認只有進程名)
top -p 1111 -p 6789//每隔5秒顯示pid是1111和pid是6789的兩個進程的資源占用情況
top -d 2 -c -p 1111//每隔2秒顯示pid是1111的進程的資源使用情況,并顯式該進程啟動的命令行參數
posted @ 2018-12-10 15:59 為自己代言 閱讀(190) | 評論 (0) | 編輯 收藏