ock4j是一個分布式鎖組件,它提供了多種不同的支持以滿足不同性能和環境的需求,基于Spring AOP的聲明式和編程式分布式鎖,支持RedisTemplate、Redisson、Zookeeper。
Gitee地址:gitee.com/baomidou/lo…[1]
<!-- Lock4j -->
<!-- 若使用redisTemplate作為分布式鎖底層,則需要引入 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
<version>2.2.4</version>
</dependency>
<!-- 若使用redisson作為分布式鎖底層,則需要引入 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redisson-spring-boot-starter</artifactId>
<version>2.2.4</version>
</dependency>
spring:
redis:
database: 0
# Redis服務器地址 寫你的ip
host: 127.0.0.1
# Redis服務器連接端口
port: 6379
# Redis服務器連接密碼(默認為空)
password:
# 連接池最大連接數(使用負值表示沒有限制 類似于mysql的連接池
jedis:
pool:
max-active: 200
# 連接池最大阻塞等待時間(使用負值表示沒有限制) 表示連接池的鏈接拿完了 現在去申請需要等待的時間
max-wait: -1
# 連接池中的最大空閑連接
max-idle: 10
# 連接池中的最小空閑連接
min-idle: 0
# 連接超時時間(毫秒) 去鏈接redis服務端
timeout: 6000
package com.baomidou.lock.annotation;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Lock4j {
String name() default "";
Class<? extends LockExecutor> executor() default LockExecutor.class;
String[] keys() default {""};
long expire() default -1L;
long acquireTimeout() default -1L;
boolean autoRelease() default true;
}
@RestController
@RequestMapping("/mock")
public class MockController {
@GetMapping("/lockMethod")
@Lock4j(keys={"#key"}, acquireTimeout=1000, expire=10000)
public Result lockMethod(@RequestParam String key) {
ThreadUtil.sleep(5000);
return Result.OK(key);
}
}
打開瀏覽器窗口,重復刷新訪問:
http://localhost:8080/mock/lockMethod?key=123
成功獲得鎖訪問結果:
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": "123",
"timestamp": 1678866083211
}
搶占不到鎖,Lock4j會拋出com.baomidou.lock.exception.LockFailureException: request failed,please retry it.異常,微信搜索公眾號:架構師指南,回復:架構師 領取資料 。通過全局異常處理返回如下結果:
{
"success": false,
"message": "操作失敗,request failed,please retry it.",
"code": 500,
"result": null,
"timestamp": 1678866034929
}
/**
* 自定義分布式鎖執行器
*
* @author: austin
* @since: 2023/3/15 15:45
*/
@Component
public class CustomRedissonLockExecutor extends AbstractLockExecutor {
@Override
public Object acquire(String lockKey, String lockValue, long expire, long acquireTimeout) {
return null;
}
@Override
public boolean releaseLock(String key, String value, Object lockInstance) {
return false;
}
}
在注解上直接指定特定的執行器:@Lock4j(executor=CustomRedissonLockExecutor.class)。
/**
* 自定義分布式鎖key生成器
*
* @author: austin
* @since: 2023/3/15 15:46
*/
@Component
public class CustomKeyBuilder extends DefaultLockKeyBuilder {
public CustomKeyBuilder(BeanFactory beanFactory) {
super(beanFactory);
}
}
/**
* 自定義搶占鎖失敗執行策略
*
* @author: austin
* @since: 2023/3/15 15:49
*/
@Component
public class GrabLockFailureStrategy implements LockFailureStrategy {
@Override
public void onLockFailure(String key, Method method, Object[] arguments) {
}
}
默認的鎖獲取失敗策略為 com.baomidou.lock.DefaultLockFailureStrategy.
@Service
public class LockServiceImpl implements LockService {
@Autowired
private LockTemplate lockTemplate;
@Override
public void lock(String resourceKey) {
LockInfo lock=lockTemplate.lock(resourceKey, 10000L, 2000L, CustomRedissonLockExecutor.class);
if (lock==null) {
// 獲取不到鎖
throw new FrameworkException("業務處理中,請稍后再試...");
}
// 獲取鎖成功,處理業務
try {
doBusiness();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lockTemplate.releaseLock(lock);
}
}
private void doBusiness() {
// TODO 業務執行邏輯
}
}
[1]Gitee開源地址: https://gitee.com/baomidou/lock4j
1.copy 和 deepcopy 的根本區別就是內存地址和數值的不同復制的區別
深淺拷貝都是對象的拷貝都會生成一個看起來與原對象相同的對象,它們的本質區別就是拷貝出來的對象地址是否和原對象一致。
2.淺拷貝是對一個對象父級外層的表面復制 并不會拷貝子級內部數據不會被復制
淺拷貝的不同數據類型
對于可變數據類型 不會拷貝深層次的索引 淺拷貝會開辟新的內存空間去存放
對于不可變的數據類型 淺拷貝對象的時候,引用對象的地址空間
3.深拷貝是對所有層級的拷貝,遞歸 內部外部都會被拷貝進去
對于可變的數據類型 深拷貝可以將內部外部全部直接復制
對于不可變數據類型 深拷貝會同淺拷貝一樣,是對地址的引用。
1.封裝 根據職責將屬性和方法封裝到一個抽象的類中定義類的準則
2.繼承 實現代碼的重用 相同的代碼不必重復的編寫 減少代碼冗余 子類繼承父類
3.多態 不同的子類調用相同的父類產生不同的結果
閉包是有兩個函數嵌套定義的, 內部函數調用外部函數的變量值,變量值加上內部還是里面的代碼組成的代碼塊組成一個新的內存空間,我們把這個空間叫做閉包 閉包=函數 + 環境變量 (在函數定義的時候定義 ,不是全局變量,這個環境變量一定要被內部函數調用才算是閉包
'''
lambda function 函數嵌套 object
1.lambda 匿名函數 能夠完成基本的簡單功能 節省創建函數對象的流程 傳遞是這個函數的引用 只有功能
2.function 普通函數 能夠完成函數的普通功能 傳遞是這個函數的引用 只有功能
3.函數嵌套 閉包 能夠完成復雜的功能函數 傳遞的是閉包函數中的函數和數據 傳遞是功能和數據
4.object 對象 能夠完成復雜的功能 可傳遞多個數據和很多功能 因此是數據+功能
'''
'''
進程線程協程的含義 區別 適用于那種場景類型
含義*
1.1.進程是系統中進行資源分配和資源調度的一個獨立單位 一個程序至少有一個進程 一個進程至少有一個線程
1.2.線程是進程的一個實體,是cpu調度和分派的基本單位 線程是比進程更小的可以獨立運行的基本單位
線程不擁有系統資源,擁有的是在運行時不可少的計數器寄存器和棧 在進程中的線程可以共享在進程中的其他資源
線程是不能獨立執行的,必須依存在進程中
1.3.協程是一種比線程更加輕量級的存在,一個線程可以有多個協程
區別*
2.1. 進程切換需要的資源很大,效率低
2.2. 線程切換需要的資源相對于進程較低, 效率相對于進程高一點
2.3. 協程切換任務資源很小,三者中效率最高。
2.4. 多進程 多線程 根據CPU核數不一樣可能是并行 但是協程在一個線程中所以是并發
適用的場景類型*
3.1. 計算密集型 進程 process
3.2. IO密集型 線程thread 協程 多線程
協程比線程快的原因
4.1. 高并發 + 高擴展性 + 低成本 一個CPU支持上萬個協程都不是問題,很適合用于高并發處理協程
所以很適用于高并發處理協程能保留上一次調用時的狀態,不管是進程還是線程,每次阻塞切換都需要陷入系統調用,
使用線程時需要小心地處理同步問題,而協程完全不存在這個問題
'''
'''
迭代器 iterator 可以將迭代對象直接迭代出來 list for迭代循環
for和迭代的區別就是 迭代是next循環出來每個值
迭代是將集合中的元素訪問出來,迭代器保存的是獲取數據的方式而不是結果所以想用的時候就可以生成
可以節省大量內存空間,它是一個可以記住遍歷的位置的對象。迭代器對象從集合的第一個元素開始訪問
直到所有的元素被訪問完結束。迭代器只能往前不能后退
迭代器的創建有兩種方式 iter() next() 字符串列表或者元組對象均可以創建迭代對象
生成器genetor 的概念
所謂生成器就是一邊循環一邊計算的機制,使用了yield的函數被稱為生成器
生成器是一個返回迭代器的函數 只能用于迭代操作 簡單理解就是生成器就是一個特殊的迭代器
在調用生成器運行的過程中遇到yield時函數會暫停并且保存當前所運行的信息 返回yield值
并且在下一次執行next方法時 從當前位置繼續執行
生成器和迭代器的區別
generator iterator
使用for循環遍歷的是可迭代對象
迭代方法就是加next方法就是迭代器
生成器是特殊的迭代器 生成器可以做到迭代器的所有動作和行為
自動創建了next和iter方法 生成器就會顯得特別簡潔高效
使用生成器表達式取代列表解析可以同時節省內存空間
為什么要使用生成器
可迭代對象(列表 字符串 元組 )所有的數據都在內存中會非常耗費內存
如果說只是想要訪問后面的數據就會嚴重耗費內存 如果說列表元素按照某種算法推算出來,
我們就可以在循環的過程中不斷推算出后續元素,
這樣不必創建完整的list 這樣可以節省空間
想要得到龐大的數據又不想占據空間的話這個時候就要使用生成器則為 iter next
生成器僅僅保存了一套生成數值的算法,并且沒有讓這個算法直接執行,我什么時候調用它,他就開始計算一個新的值并且給你返回。
什么是裝飾器 為什么要使用它
裝飾器本身是函數 這個函數的主要作用是包裝另外一個函數或者類
包裝的目的是不改變原來函數的情況下改變被包裝對象的行為
接受一個函數,內部進行包裝,然后返回一個新函數
通過高階函數傳遞函數參數,新函數增加舊函數的需求,然后執行舊函數
在django中middleware中間件 其實就是高級的裝飾器
什么是GIL鎖*
global lock GIL鎖是python中的全局解釋器鎖,一個進程中有多個線程,當這個線程運行的時候就會霸占GIL鎖
當霸占這個GIL鎖的時候就會阻止其他線程繼續運行,當這個線程執行完之后,才會繼續執行之后的線程。
如果線程中遇到耗時操作IO密集型任務,解釋器鎖會解開,使得其他線程運行。
所以說在多線程中,線程的運行是有先后順序的并不是同時進行
什么時候釋放GIL鎖*
1.時間片耗盡 cpu時間
2.任務遇到IO等待時間
3.執行任務結束
4.執行到字節碼閾值時
互斥鎖和GIL的區別
互斥鎖在多線程的情況下,確保當前線程執行完之后,繼續下個任務,如果說當前任務仍然在執行的時候,下個任務會阻塞
GIL鎖是保證在同一時間有一個線程,當釋放掉GIL的時候,會繼續下一個線程
但是也有可能是IO流阻塞,并沒有完成該線程任務就直接釋放,該線程的任務分多少次執行完這個會安裝GIL默認策略。
互斥鎖是必須保證當前任務在當前線程的完成
GIL鎖是不一定在當前線程完成任務 情況是IO流阻塞的時候,直接停止當前線程繼續下次線程
條堅持更文第六天,進行MybatisPlus的學習,歡迎小伙伴們的關注,讓我們一起努力
Code皮皮蝦 一個沙雕而又有趣的憨憨少年,和大多數小伙伴們一樣喜歡聽歌、游戲,當然除此之外還有寫作的興趣,emm...,日子還很長,讓我們結伴一起走下去吧
歡迎各位小伙伴們關注我的公眾號?:JavaCodes,名稱雖帶Java但涉及范圍可不止Java領域噢,期待您的關注
?MybatisPlus系列文章?
MybatisPlus學習筆記(一)環境搭建及入門HelloWorld
MybatisPlus保姆級學習筆記(二)CRUD全套詳解
MybatisPlus學習筆記(三)實現邏輯刪除、分頁
MybatisPlus保姆級學習筆記(四)條件構造器Wrapper方法詳解
MybatisPlus學習筆記(五)實現樂觀鎖機制
1、代碼生成器簡介
AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。
MyBatis-Plus 從 3.0.3 之后移除了代碼生成器與模板引擎的默認依賴,需要手動添加相關依賴:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
添加 模板引擎 依賴
MyBatis-Plus 支持 Velocity(默認)、Freemarker、Beetl,用戶可以選擇自己熟悉的模板引擎,如果都不滿足您的要求,可以采用自定義模板引擎。我這里就是使用自定義模板
Velocity(默認)
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
Freemarker:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>latest-freemarker-version</version>
</dependency>
Beetl:
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>latest-beetl-version</version>
</dependency>
注意!如果您選擇了非默認引擎,需要在 AutoGenerator 中 設置模板引擎。
AutoGenerator generator=new AutoGenerator();
// set freemarker engine
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// set beetl engine
generator.setTemplateEngine(new BeetlTemplateEngine());
// set custom engine (reference class is your custom engine class)
generator.setTemplateEngine(new CustomTemplateEngine());
// other config
...
編寫配置
MyBatis-Plus 的代碼生成器提供了大量的自定義參數供用戶選擇,能夠滿足絕大部分人的使用需求。
public class CodeGenerator {
public static void main(String[] args) {
//創建代碼生成器
AutoGenerator mpg=new AutoGenerator();
//全局配置
GlobalConfig gc=new GlobalConfig();
//獲取當前系統目錄
String projectPath=System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("dong"); //生成作者注釋
gc.setOpen(false); //生成后是否打開資源管理器
gc.setFileOverride(false); //重新生成時文件是否覆蓋
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER); //主鍵策略
gc.setDateType(DateType.ONLY_DATE);//定義生成的實體類中日期類型
gc.setSwagger2(false);//開啟Swagger2模式
mpg.setGlobalConfig(gc);
// 3、數據源配置
DataSourceConfig dsc=new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mp?serverTimezone=GMT%2B8"); //url
dsc.setDriverName("com.mysql.cj.jdbc.Driver"); //數據庫驅動
dsc.setUsername("root"); //數據庫賬號
dsc.setPassword("root"); //數據庫密碼
dsc.setDbType(DbType.MYSQL); //數據庫類型
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc=new PackageConfig();
pc.setModuleName("test"); //模塊名,可以不設置
pc.setParent("cn.dong"); //放在哪個包下
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy=new StrategyConfig();
strategy.setInclude("employee");//對哪一張表生成代碼
strategy.setNaming(NamingStrategy.underline_to_camel);//數據庫表映射到實體的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成實體時去掉表前綴
strategy.setLogicDeleteFieldName("deleted"); //邏輯刪除字段設置
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//數據庫表字段映射到實體的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain=true) setter鏈式操作
strategy.setControllerMappingHyphenStyle(true); //url中駝峰轉連字符
mpg.setStrategy(strategy);
//執行
mpg.execute();
}
}
Figure :
Figure :
Figure :
Figure :
Figure :
Figure :
最后
我是 Code皮皮蝦,一個熱愛分享知識的 皮皮蝦愛好者,未來的日子里會不斷更新出對大家有益的博文,公眾號:JavaCodes,期待大家的關注?。?!
創作不易,如果這篇博文對各位有幫助,希望各位小伙伴可以一鍵三連哦!,感謝支持,我們下次再見~~~