函數(shù)式編程概念
函數(shù)式編程是一種編程的范式和編程的方法論(programming paradigm)莺葫,它屬于結(jié)構(gòu)化編程的一種莺奸,主要的思想是把運算的過程盡量通過一組嵌套的函數(shù)來實現(xiàn)败徊。
函數(shù)式編程的幾個特點:
- 函數(shù)可以作為變量、參數(shù)续滋、返回值和數(shù)據(jù)類型舅锄。
- 基于表達式來替代方法的調(diào)用
- 函數(shù)無狀態(tài)躯枢,可以并發(fā)和獨立使用
- 函數(shù)無副作用则吟,不會修改外部的變量
- 函數(shù)結(jié)果確定性;同樣的輸入闺金,必然會有同樣的結(jié)果逾滥。
函數(shù)式編程的優(yōu)點:
- 代碼簡潔,開發(fā)效率高
- 接近自然語言败匹,易于理解
- 由于函數(shù)的特性寨昙,易于調(diào)試和使用
- 易于并發(fā)使用
- 腳本語言的特性,易于升級部署
@FunctionalInterface 函數(shù)式接口
@FunctionalInterface是 Java 8 新加入的一種接口掀亩,注解在接口層面舔哪,且注解的接口要有且僅有一個抽象方法。具體就是說槽棍,注解在Inteface上捉蚤,且interface里只能有一個抽象方法,可以有多個default方法炼七。
函數(shù)式接口的一大特性就是可以被lambda表達式和函數(shù)引用表達式代替
Lambda 表達式
Lambda 表達式是一種匿名函數(shù)(對 Java 而言這并不完全正確缆巧,但現(xiàn)在姑且這么認為),簡單地說豌拙,它是沒有聲明的方法陕悬,也即沒有訪問修飾符、返回值聲明和名字按傅。
你可以將其想做一種速記捉超,在你需要使用某個方法的地方寫上它胧卤。當某個方法只使用一次,而且定義很簡短拼岳,使用這種速記替代之尤其有效枝誊,這樣,你就不必在類中費力寫聲明與方法了惜纸。
使用場景
Redis工具類
JAVA是面向?qū)ο蟮囊度觯ǔ7椒ǖ娜雲(yún)⒍际穷悾▽ο螅蛘咦兞靠安荆瘮?shù)式編程痊乾,就是把一個函數(shù)(方法)作為入?yún)⑵け冢沁@個有啥好處呢椭更??
簡單舉個例子蛾魄,
當多個方法都有同樣的操作時虑瀑,我們通常想的是將其共同抽象成獨立方法,但是整個流程是一樣的滴须,只是不同場景下舌狗,具體業(yè)務處理處理不同時,我們該怎么抽象呢扔水?如果像下面那樣操作痛侍,明顯就是破壞了整個業(yè)務流程
public Object common1(){
return "common1";
}
public Object common2(){
return "common2";
}
public void method1(Object o){
Object o1 =this.common1();
//doSomeing
System.out.println("========"+o1);
this.common2();
}
public void method2(Object o){
Object o1 =this.common1();
//doSomeing
System.out.println("-----------"+o1);
this.common2();
}
那想再不破壞整個流程的情況改怎么處理呢?可以利用函數(shù)式編程魔市,把接口作為入?yún)⒅鹘欤斁唧w業(yè)務處理時再去實現(xiàn)其具體業(yè)務。
@FunctionalInterface
public interface Operation<T,R> {
public T operate(R r);
}
public void common(Operation<Object,Object> operation){
//step1
Object o1 =this.common1();
operation.operate(o1);
//step3
this.common2();
}
public void method1Operation(Object o){
this.common(o1 -> "========"+o1);
}
public void method2Operation(Object o){
this.common(o1 -> "========"+o1);
}
上面的介紹過于抽象待德,下面介紹一個很實用的場景君丁。
對于一些池的操作,比如redisPool将宪,或者線程池绘闷,都有一些通用的操作,首先较坛,先從池中取出對象印蔗,然后實現(xiàn)具體業(yè)務,然后再把對象放入池中丑勤;
可以看出這里有操作流程上有重復的地方华嘹,如果我們把這寫都寫在具體業(yè)務中,過于耦合和繁瑣确封,那我們就可以像上面的demo一樣除呵,將其公用部分抽象出來再菊,這里已redisPool為例,如下
@FunctionalInterface
public interface Operation<T,R> {
public T operate(R r);
}
public class RedisTool2 {
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
//NX|XX, NX -- Only set the key if it does not already exist;
// XX -- Only set the key if it already exist.
private static final String SET_IF_NOT_EXIST = "NX";
//EX|PX, expire time units: EX = seconds; PX = milliseconds
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static volatile JedisPool jedisPool = null;
public static JedisPool getRedisPoolUtil() {
if(null == jedisPool ){
synchronized (RedisTool2.class){
if(null == jedisPool){
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(10);
poolConfig.setMaxWaitMillis(100*1000);
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig,"192.168.10.151",6379);
}
}
}
return jedisPool;
}
public static <T> T doOperation(Operation<T,Jedis> operation){
Jedis jedis = jedisPool.getResource();
try {
return operation.operate(jedis);
}catch (Exception e){
return null;
}finally {
jedisPool.returnResource(jedis);
}
}
//使用匿名內(nèi)部類實現(xiàn)
public static boolean tryGetDistributedLock1(final String lockKey, final String requestId, final int expireTime) {
return doOperation(new Operation<Boolean, Jedis>() {
public Boolean operate(Jedis jedis) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
});
}
//使用lambda表達式實現(xiàn)
public static boolean tryGetDistributedLock2(final String lockKey, final String requestId, final int expireTime) {
return doOperation(jedis ->{
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
});
}
//使用lambda表達式實現(xiàn)
public static boolean tryGetDistributedLock2(final String lockKey, final String requestId, final int expireTime) {
String result = doOperation(jedis ->jedis.set(lockKey, requestId, SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME, expireTime));
return LOCK_SUCCESS.equals(result);
}
public boolean releaseDistributedLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = this.execute(jedis ->jedis.eval(script, Collections.singletonList(COMMON_LOCK_KEY+lockKey), Collections.singletonList(requestId)));
return RELEASE_SUCCESS.equals(result);
}
//普通方法
public static boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
Jedis jedis = jedisPool.getResource();
try {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}catch (Exception e){
return false;
}finally {
jedisPool.returnResource(jedis);
}
}
}
分布式定時任務
@FunctionalInterface
public interface Operation {
public void execJob();
}
抽象基類:把獲取鎖和釋放鎖抽象到寄類實現(xiàn)颜曾,在具體業(yè)務job不用關(guān)心這些
@Component
public abstract class AbstractBasicTask {
private static final Logger logger = LoggerFactory.getLogger(AbstractBasicTask.class);
@Autowired
RedisService redisService;
public void doOperation(String taskName,Operation operation){
String requestId = DateUtils.getNowTimeMill();
// 控制并發(fā)鎖
if (redisService.tryGetDistributedLock(taskName, requestId,600)) {
long start = System.currentTimeMillis();
try {
// 開始執(zhí)行定時任務
operation.execJob();
logger.info("{}:執(zhí)行定時任務完成纠拔,耗時(毫秒):{}", taskName, (System.currentTimeMillis() - start));
} catch (Exception e) {
logger.error(taskName + ":執(zhí)行定時任務異常", e);
} finally {
// 釋放鎖
try {
redisService.releaseDistributedLock(taskName,requestId);
} catch (Exception e) {
logger.error(taskName + ":釋放鎖異常", e);
}
}
} else {
logger.info("{}:獲取鎖失敗", taskName);
}
}
/**
* 執(zhí)行JOB業(yè)務邏輯
*/
public abstract void exec();
具體執(zhí)行任務demoJob
@Component
@EnableScheduling
public class demoJob extends AbstractBasicTask{
@Scheduled(cron = "1 * * * * ?")
@Override
public void exec() {
this.doOperation("demoJob", this::testA);
}
private void testA(){
System.out.println("=========");
}
}
總結(jié):比較常用的,典型的應用場景泛豪,是當我們運算的過程可以抽象成好幾個步驟時稠诲,把其中相同部分,抽象成公共方法(像上面的common方法)诡曙,并且把函數(shù)式接口作為其入?yún)⑼涡穑诰唧w業(yè)務實現(xiàn)中,使用lambda表達式實現(xiàn)具體業(yè)務實現(xiàn)(像上面的method1Operation价卤、method2Operation)劝萤。