CompletableFuture进阶
CompletableFuture进阶
1、异步任务的交互
异步任务交互指将异步任务获取结果的速度相比较,按一定的规则( 先到先用 )进行下一步处理。
1.1 applyToEither
applyToEither()
把两个异步任务做比较,异步任务先到结果的,就对先到的结果进行下一步的操作。
CompletableFuture<R> applyToEither(CompletableFuture<T> other, Function<T,R> func)
演示案例:使用最先完成的异步任务的结果
public class ApplyToEitherDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 开启异步任务1
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int x = new Random().nextInt(3);
CommonUtils.sleepSecond(x);
CommonUtils.printThreadLog("任务1耗时:" + x + "秒");
return x;
});
// 开启异步任务2
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
int y = new Random().nextInt(3);
CommonUtils.sleepSecond(y);
CommonUtils.printThreadLog("任务2耗时:" + y + "秒");
return y;
});
// 哪些异步任务的结果先到达,就使用哪个异步任务的结果
CompletableFuture<Integer> future = future1.applyToEither(future2, (result -> {
CommonUtils.printThreadLog("最先到达的结果:" + result);
return result;
}));
// 主线程休眠4秒,等待所有异步任务完成
CommonUtils.sleepSecond(4);
Integer ret = future.get();
CommonUtils.printThreadLog("ret = " + ret);
}
}
速记心法:任务1、任务2就像两辆公交,哪路公交先到,就乘坐(使用)哪路公交。
以下是applyToEither 和其对应的异步回调版本
CompletableFuture<R> applyToEither(CompletableFuture<T> other, Function<T,R> func)
CompletableFuture<R> applyToEitherAsync(CompletableFuture<T> other, Function<T,R> func)
CompletableFuture<R> applyToEitherAsync(CompletableFuture<T> other, Function<T,R> func,Executor executor)
1.2 acceptEither
acceptEither()
把两个异步任务做比较,异步任务先到结果的,就对先到的结果进行下一步操作 ( 消费使用 )。
CompletableFuture<Void> acceptEither(CompletableFuture<T> other, Consumer<T> action)
CompletableFuture<Void> acceptEitherAsync(CompletableFuture<T> other, Consumer<T> action)
CompletableFuture<Void> acceptEitherAsync(CompletableFuture<T> other, Consumer<T> action,Executor executor)
演示案例:使用最先完成的异步任务的结果
public class AcceptEitherDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 异步任务交互
CommonUtils.printThreadLog("main start");
// 开启异步任务1
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
int x = new Random().nextInt(3);
CommonUtils.sleepSecond(x);
CommonUtils.printThreadLog("任务1耗时:" + x + "秒");
return x;
});
// 开启异步任务2
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
int y = new Random().nextInt(3);
CommonUtils.sleepSecond(y);
CommonUtils.printThreadLog("任务2耗时:" + y + "秒");
return y;
});
// 哪些异步任务的结果先到达,就使用哪个异步任务的结果
future1.acceptEither(future2,result -> {
CommonUtils.printThreadLog("最先到达的结果:" + result);
});
// 主线程休眠4秒,等待所有异步任务完成
CommonUtils.sleepSecond(4);
CommonUtils.printThreadLog("main end");
}
}
1.3 runAfterEither
如果不关心最先到达的结果,只想在有一个异步任务先完成时得到完成的通知,可以使用 runAfterEither()
,以下是它的相关方法:
CompletableFuture<Void> runAfterEither(CompletableFuture<T> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletableFuture<T> other, Runnable action)
CompletableFuture<Void> runAfterEitherAsync(CompletableFuture<T> other, Runnable action, Executor executor)
提示
异步任务交互的三个方法和之前学习的异步的回调方法 thenApply、thenAccept、thenRun 有异曲同工之妙。
2、get() 和 join() 区别
get() 和 join() 都是CompletableFuture提供的以阻塞方式获取结果的方法。
那么该如何选用呢?请看如下案例:
public class GetOrJoinDemo {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "hello";
});
String ret = null;
// 抛出检查时异常,必须处理
try {
ret = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("ret = " + ret);
// 抛出运行时异常,可以不处理
ret = future.join();
System.out.println("ret = " + ret);
}
}
使用时,我们发现,get() 抛出检查时异常 ,需要程序必须处理;而join() 方法抛出运行时异常,程序可以不处理。所以,join() 更适合用在流式编程中。
3、ParallelStream VS CompletableFuture
CompletableFuture 虽然提高了任务并行处理的能力,如果它和 Stream API 结合使用,能否进一步多个任务的并行处理能力呢?
同时,对于 Stream API 本身就提供了并行流ParallelStream,它们有什么不同呢?
我们将通过一个耗时的任务来体现它们的不同,更重要地是,我们能进一步加强 CompletableFuture 和 Stream API 的结合使用,同时搞清楚CompletableFuture 在流式操作的优势
需求:创建10个MyTask耗时的任务,统计它们执行完的总耗时
定义一个MyTask类,来模拟耗时的长任务
public class MyTask {
private int duration;
public MyTask(int duration) {
this.duration = duration;
}
// 模拟耗时的长任务
public int doWork() {
CommonUtils.printThreadLog("doWork");
CommonUtils.sleepSecond(duration);
return duration;
}
}
同时,我们创建10个任务,每个持续1秒。
IntStream intStream = IntStream.range(0, 10);
List<MyTask> tasks = intStream.mapToObj(item -> {
return new MyTask(1);
}).collect(Collectors.toList());
3.1 并行流的局限
我们先使用串行执行,让所有的任务都在主线程 main 中执行。
public class SequenceDemo {
public static void main(String[] args) {
// 方案一:在主线程中使用串行执行
// step 1: 创建10个MyTask对象,每个任务持续1s,存入list集合便于启动Stream操作
IntStream intStream = IntStream.range(0, 10);
List<MyTask> tasks = intStream.mapToObj(item -> {
return new MyTask(1);
}).collect(Collectors.toList());
// step 2: 执行tasks集合中的每个任务,统计总耗时
long start = System.currentTimeMillis();
List<Integer> result = tasks.stream().map(myTask -> {
return myTask.doWork();
}).collect(Collectors.toList());
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("processed %d tasks cost %.2f second",tasks.size(),costTime);
}
}
它花费了10秒, 因为每个任务在主线程一个接一个的执行。
因为涉及 Stream API,而且存在耗时的长任务,所以,我们可以使用 parallelStream()
public class ParallelDemo {
public static void main(String[] args) {
// 方案二:使用并行流
// step 1: 创建10个MyTask对象,每个任务持续1s,存入List集合
IntStream intStream = IntStream.range(0, 10);
List<MyTask> tasks = intStream.mapToObj(item -> {
return new MyTask(1);
}).collect(Collectors.toList());
// step 2: 执行10个MyTask,统计总耗时
long start = System.currentTimeMillis();
List<Integer> results = tasks.parallelStream().map(myTask -> {
return myTask.doWork();
}).collect(Collectors.toList());
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("processed %d tasks %.2f second",tasks.size(),costTime);
}
}
它花费了2秒多,因为此次并行执行使用了8个线程 (7个是ForkJoinPool线程池中的, 一个是 main 线程),需要注意是:运行结果由自己电脑CPU的核数决定。
3.2 CompletableFuture 在流式操作的优势
让我们看看使用CompletableFuture是否执行的更有效率
public class CompletableFutureDemo {
public static void main(String[] args) {
// 需求:创建10MyTask耗时的任务,统计它们执行完的总耗时
// 方案三:使用CompletableFuture
// step 1: 创建10个MyTask对象,每个任务持续1s,存入List集合
IntStream intStream = IntStream.range(0, 10);
List<MyTask> tasks = intStream.mapToObj(item -> {
return new MyTask(1);
}).collect(Collectors.toList());
// step 2: 根据MyTask对象构建10个耗时的异步任务
long start = System.currentTimeMillis();
List<CompletableFuture<Integer>> futures = tasks.stream().map(myTask -> {
return CompletableFuture.supplyAsync(() -> {
return myTask.doWork();
});
}).collect(Collectors.toList());
// step 3: 当所有任务完成时,获取每个异步任务的执行结果,存入List集合中
List<Integer> results = futures.stream().map(future -> {
return future.join();
}).collect(Collectors.toList());
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("processed %d tasks cost %.2f second",tasks.size(),costTime);
}
}
运行发现,两者使用的时间大致一样。能否进一步优化呢?
CompletableFutures 比 ParallelStream 优点之一是你可以指定Executor去处理任务。你能选择更合适数量的线程。我们可以选择大于Runtime.getRuntime().availableProcessors() 数量的线程,如下所示:
public class CompletableFutureDemo2 {
public static void main(String[] args) {
// 需求:创建10MyTask耗时的任务,统计它们执行完的总耗时
// 方案三:使用CompletableFuture
// step 1: 创建10个MyTask对象,每个任务持续1s,存入List集合
IntStream intStream = IntStream.range(0, 10);
List<MyTask> tasks = intStream.mapToObj(item -> {
return new MyTask(1);
}).collect(Collectors.toList());
// 准备线程池
final int N_CPU = Runtime.getRuntime().availableProcessors();
// 设置线程池的数量最少是10个,最大是16个
ExecutorService executor = Executors.newFixedThreadPool(Math.min(tasks.size(), N_CPU * 2));
// step 2: 根据MyTask对象构建10个耗时的异步任务
long start = System.currentTimeMillis();
List<CompletableFuture<Integer>> futures = tasks.stream().map(myTask -> {
return CompletableFuture.supplyAsync(() -> {
return myTask.doWork();
},executor);
}).collect(Collectors.toList());
// step 3: 当所有任务完成时,获取每个异步任务的执行结果,存入List集合中
List<Integer> results = futures.stream().map(future -> {
return future.join();
}).collect(Collectors.toList());
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("processed %d tasks cost %.2f second",tasks.size(),costTime);
// 关闭线程池
executor.shutdown();
}
}
测试代码时,电脑配置是4核8线程,而我们创建的线程池中线程数最少也是10个,所以,每个线程负责一个任务( 耗时1s ),总体来说,处理10个任务总共需要约1秒。
3.3 合理配置线程池中的线程数
正如我们看到的,CompletableFuture 可以更好地控制线程池中线程的数量,而 ParallelStream 不能。
问题1:如何选用 CompletableFuture 和 ParallelStream ?
如果你的任务是IO密集型的,你应该使用CompletableFuture;
如果你的任务是CPU密集型的,使用比处理器更多的线程是没有意义的,所以选择ParallelStream ,因为它不需要创建线程池,更容易使用。
问题2:IO密集型任务和CPU密集型任务的区别?
CPU密集型也叫计算密集型,此时,系统运行时大部分的状况是CPU占用率近乎100%,I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU 使用率很高。比如说要计算1+2+3+…+ 10万亿、天文计算、圆周率后几十位等, 都是属于CPU密集型程序。
CPU密集型任务的特点:大量计算,CPU占用率一般都很高,I/O时间很短
IO密集型指大部分的状况是CPU在等I/O (硬盘/内存) 的读写操作,但CPU的使用率不高。
简单的说,就是需要大量的输入输出,例如读写文件、传输文件、网络请求。
IO密集型任务的特点:大量网络请求,文件操作,CPU运算少,很多时候CPU在等待资源才能进一步操作。
问题3:既然要控制线程池中线程的数量,多少合适呢?
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 Ncpu+1
如果是IO密集型任务,参考值可以设置为 2 * Ncpu,其中Ncpu 表示 核心数。
注意的是:以上给的是参考值,详细配置超出本次课程的范围,选不赘述。
4、大数据商品比价
4.1 需求描述和分析
需求描述: 实现一个大数据比价服务,价格数据可以从京东、天猫、拼多多等平台去获取指定商品的价格、优惠金额,然后计算出实际付款金额 ( 商品价格 - 优惠金额 ),最终返回价格最优的平台与价格信息。
4.2 构建工具类和实体类
定义价格实体类 PriceResult
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class PriceResult {
private int price; // 平台价格
private int discount; // 折扣
private int realPrice; // 最终价
private String platform; // 商品平台
public PriceResult(String platform) {
this.platform = platform;
}
@Override
public String toString() {
return "PriceResult{" +
"平台=" + platform +
", 价格=" + price +
", 折扣=" + discount +
", 最终价=" + realPrice +
'}';
}
}
修改工具类 CommonUtils,添加 getCurrentTime() 方法获取当前时间并格式化,修改 printThreadLog() 方法,把时间戳替换成当前时间。
public class CommonUtils {
private static String getCurrentTime() {
LocalTime time = LocalTime.now();
return time.format(DateTimeFormatter.ofPattern("[HH:mm:ss.SSS]"));
}
// 打印输出带线程信息的日志
public static void printThreadLog(String message) {
// 时间戳 | 线程id | 线程名 | 日志信息
String result = new StringJoiner(" | ")
.add(getCurrentTime())
.add(String.format("%2d", Thread.currentThread().getId()))
.add(Thread.currentThread().getName())
.add(message)
.toString();
System.out.println(result);
}
}
4.3 构建 HttpRequest
HttpRequest 用于模拟网络请求 ( 耗时的操作 )
public class HttpRequest {
// 获取指定商品的淘宝价
public static PriceResult getTaoBaoPrice(String productName) {
CommonUtils.printThreadLog("获取淘宝上" + productName + "价格");
mockCostTimeOperation();
PriceResult priceResult = new PriceResult("淘宝");
priceResult.setPrice(5199);
CommonUtils.printThreadLog("获取淘宝上" + productName + "价格完成:5199");
return priceResult;
}
// 获取指定商品的淘宝优惠
public static int getTaoBaoDiscount(String productName) {
CommonUtils.printThreadLog("获取淘宝上" + productName + "优惠");
mockCostTimeOperation();
CommonUtils.printThreadLog("获取淘宝上" + productName + "优惠完成:-200");
return 200;
}
// 获取指定商品的JD价
public static PriceResult getJDongPrice(String productName) {
CommonUtils.printThreadLog("获取京东上" + productName + "价格");
mockCostTimeOperation();
PriceResult priceResult = new PriceResult("京东");
priceResult.setPrice(5299);
CommonUtils.printThreadLog("获取京东上" + productName + "价格完成:5299");
return priceResult;
}
// 获取指定商品的JD优惠
public static int getJDongDiscount(String productName) {
CommonUtils.printThreadLog("获取京东上" + productName + "优惠");
mockCostTimeOperation();
CommonUtils.printThreadLog("获取京东上" + productName + "优惠完成:-150");
return 150;
}
// 获取指定商品的拼多多价
public static PriceResult getPDDPrice(String productName) {
CommonUtils.printThreadLog("获取拼多多上" + productName + "价格");
mockCostTimeOperation();
PriceResult priceResult = new PriceResult("拼多多");
priceResult.setPrice(5399);
CommonUtils.printThreadLog("获取拼多多上" + productName + "价格完成:5399");
return priceResult;
}
// 获取指定商品的拼多多优惠
public static int getPDDDiscount(String productName) {
CommonUtils.printThreadLog("获取拼多多上" + productName + "优惠");
mockCostTimeOperation();
CommonUtils.printThreadLog("获取拼多多上" + productName + "优惠完成:-5300");
return 5300;
}
// 模拟耗时的操作
private static void mockCostTimeOperation() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.4 使用串行方式操作商品比价
public class ComparePriceService {
// 方案一:串行方式操作商品比价
public PriceResult getCheapestPlatformPrice(String productName) {
PriceResult priceResult;
int discount;
// 获取淘宝平台的价格和优惠
priceResult = HttpRequest.getTaoBaoPrice(productName);
discount = HttpRequest.getTaoBaoDiscount(productName);
PriceResult taoBaoPriceResult = this.computeRealPrice(priceResult, discount);
// 获取京东平台的价格和优惠
priceResult = HttpRequest.getJDongPrice(productName);
discount = HttpRequest.getJDongDiscount(productName);
PriceResult jDongPriceResult = this.computeRealPrice(priceResult, discount);
// 获取拼多多平台的价格和优惠
priceResult = HttpRequest.getPDDPrice(productName);
discount = HttpRequest.getPDDDiscount(productName);
PriceResult pddPriceResult = this.computeRealPrice(priceResult, discount);
Stream<PriceResult> stream = Stream.of(taoBaoPriceResult, jDongPriceResult, pddPriceResult);
Optional<PriceResult> minOpt = stream.min(Comparator.comparing(PriceResult::getRealPrice));
return minOpt.get();
}
// 计算商品的最终价格 = 平台价格 - 优惠价
public PriceResult computeRealPrice(PriceResult priceResult,int discount) {
priceResult.setRealPrice(priceResult.getPrice() - discount);
priceResult.setDiscount(discount);
LogUtils.printLog(priceResult.getPlatform() + "最终价格计算完成:" + priceResult.getRealPrice());
return priceResult;
}
}
使用串行方式在main线程中执行的测试类
public class ComparePriceDemo {
public static void main(String[] args) {
// 方案一测试:串行方式操作商品比价
ComparePriceService service = new ComparePriceService();
long start = System.currentTimeMillis();
PriceResult priceResult = service.getCheapestPlatformPrice("iPhone");
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("cost %.2f second \n",costTime);
System.out.println("priceResult = " + priceResult);
}
}
4.5 使用Future+线程池增强并行
public class ComparePriceService {
// 方案二:使用Future+线程池增强并行
public PriceResult getCheapestPlatformPrice2(String productName) {
// 线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 获取淘宝平台的价格和优惠
Future<PriceResult> taoBaoFuture = executor.submit(() -> {
PriceResult priceResult = HttpRequest.getTaoBaoPrice(productName);
int discount = HttpRequest.getTaoBaoDiscount(productName);
return this.computeRealPrice(priceResult, discount);
});
// 获取京东平台的价格和优惠
Future<PriceResult> jDongFuture = executor.submit(() -> {
PriceResult priceResult = HttpRequest.getJDongPrice(productName);
int discount = HttpRequest.getJDongDiscount(productName);
return this.computeRealPrice(priceResult, discount);
});
// 获取拼多多平台的价格和优惠
Future<PriceResult> pddFuture = executor.submit(() -> {
PriceResult priceResult = HttpRequest.getPDDPrice(productName);
int discount = HttpRequest.getPDDDiscount(productName);
return this.computeRealPrice(priceResult, discount);
});
// 比较计算最便宜的平台和价格
return Stream.of(taoBaoFuture, jDongFuture, pddFuture)
.map(future -> {
try {
return future.get();
} catch (Exception e) {
e.printStackTrace();
return null;
}
})
.filter(Objects::nonNull)
.min(Comparator.comparing(PriceResult::getRealPrice))
.get();
}
}
使用Future+线程池的方式的测试类
public class ComparePriceDemo {
public static void main(String[] args) {
ComparePriceService service = new ComparePriceService();
// 方案二测试:使用Future+线程池增强并行
long start = System.currentTimeMillis();
PriceResult priceResult = service.getCheapestPlatformPrice2("iPhone");
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("cost %.2f second \n",costTime);
System.out.println("priceResult = " + priceResult);
}
}
4.6 使用 CompletableFuture 进一步增强并行
public class ComparePriceService {
// 方案三:使用 CompletableFuture 进一步增强并行
public PriceResult getCheapestPlatformPrice3(String productName) {
// 获取淘宝平台的价格和优惠
CompletableFuture<PriceResult> taoBaoCF = CompletableFuture
.supplyAsync(() -> HttpRequest.getTaoBaoPrice(productName))
.thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getTaoBaoDiscount(productName)), this::computeRealPrice);
// 获取京东平台的价格和优惠
CompletableFuture<PriceResult> jDongCF = CompletableFuture
.supplyAsync(() -> HttpRequest.getJDongPrice(productName))
.thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getJDongDiscount(productName)), this::computeRealPrice);
// 获取拼多多平台的价格和优惠
CompletableFuture<PriceResult> pddCF = CompletableFuture
.supplyAsync(() -> HttpRequest.getPDDPrice(productName))
.thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getPDDDiscount(productName)), this::computeRealPrice);
return Stream.of(taoBaoCF,jDongCF,pddCF)
.map(CompletableFuture::join)
.min(Comparator.comparing(PriceResult::getRealPrice))
.get();
}
}
使用CompletableFuture方案的测试类
public class ComparePriceDemo {
public static void main(String[] args) {
ComparePriceService service = new ComparePriceService();
// 方案三测试:使用 CompletableFuture 进一步增强并行
long start = System.currentTimeMillis();
PriceResult priceResult = service.getCheapestPlatformPrice3("iPhone");
long end = System.currentTimeMillis();
double costTime = (end - start) / 1000.0;
System.out.printf("cost %.2f second \n",costTime);
System.out.println("priceResult = " + priceResult);
}
}
4.7 Stream API 操作批量商品比价
public class ComparePriceService {
public PriceResult batchComparePrice(List<String> products) {
// 遍历每个商品,根据商品开启异步任务获取最终价,然后归集到List集合
List<CompletableFuture<PriceResult>> completableFutures = products.stream()
.map(product -> {
return CompletableFuture
.supplyAsync(() -> HttpRequest.getTaoBaoPrice(product))
.thenCombine(CompletableFuture.supplyAsync(() -> HttpRequest.getTaoBaoDiscount(product)), this::computeRealPrice);
}).collect(Collectors.toList());
// 把多个商品的最终价进行排序比较获取最小值
return completableFutures
.stream()
.map(CompletableFuture::join)
.sorted(Comparator.comparing(PriceResult::getRealPrice))
.findFirst()
.get();
}
}
批量商品比价查询测试类
public class ComparePriceDemo {
public static void main(String[] args) {
ComparePriceService service = new ComparePriceService();
// 测试在一个平台比较同款产品(iPhone14)不同色系的价格
List<String> products = Arrays.asList("iPhone14黑色", "iPhone14白色", "iPhone14玫瑰红");
PriceResult priceResult = service.batchComparePrice(products);
System.out.println("priceResult = " + priceResult);
}
}
CompletableFuture进阶的更多相关文章
- Java并发编程--基础进阶高级(完结)
Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...
- 思维导图学《On Java》基础卷 + 进阶卷
说明 目录 思维导图 导读 第 1 章 什么是对象 第 3 章 一切都是对象 第 6 章 初始化和清理 第 7 章 实现隐藏 第 8 章 复用 第 9 章 多态 第 10 章 接口 第 11 章 内部 ...
- nodejs进阶(6)—连接MySQL数据库
1. 建库连库 连接MySQL数据库需要安装支持 npm install mysql 我们需要提前安装按mysql sever端 建一个数据库mydb1 mysql> CREATE DATABA ...
- nodejs进阶(4)—读取图片到页面
我们先实现从指定路径读取图片然后输出到页面的功能. 先准备一张图片imgs/dog.jpg. file.js里面继续添加readImg方法,在这里注意读写的时候都需要声明'binary'.(file. ...
- JavaScript进阶之路(一)初学者的开始
一:写在前面的问题和话 一个javascript初学者的进阶之路! 背景:3年后端(ASP.NET)工作经验,javascript水平一般般,前端水平一般般.学习资料:犀牛书. 如有误导,或者错误的地 ...
- nodejs进阶(3)—路由处理
1. url.parse(url)解析 该方法将一个URL字符串转换成对象并返回. url.parse(urlStr, [parseQueryString], [slashesDenoteHost]) ...
- nodejs进阶(5)—接收请求参数
1. get请求参数接收 我们简单举一个需要接收参数的例子 如果有个查找功能,查找关键词需要从url里接收,http://localhost:8000/search?keyword=地球.通过前面的进 ...
- nodejs进阶(1)—输出hello world
下面将带领大家一步步学习nodejs,知道怎么使用nodejs搭建服务器,响应get/post请求,连接数据库等. 搭建服务器页面输出hello world var http = require ...
- [C#] 进阶 - LINQ 标准查询操作概述
LINQ 标准查询操作概述 序 “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法.大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了IEnumerable<T> ...
- Java 进阶 hello world! - 中级程序员之路
Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...
随机推荐
- 《小白WEB安全入门》02. 开发篇
@ 目录 初识HTML潜在漏洞 初识CSS潜在漏洞 初识JS潜在漏洞 初识后端潜在漏洞 后端能做什么 后端种类 后端框架 潜在漏洞 本系列文章只叙述一些超级基础理论知识,极少有实践部分 本文涉及到的语 ...
- api接口对接如何实现,php如何对接api
API接口对接是现代软件开发中不可或缺的一部分,它允许不同的应用程序之间进行数据交换和服务调用.在PHP中,可以使用多种方式实现API接口的对接,包括基于HTTP协议的传统方法以及现代的API客户端库 ...
- LeetCode 周赛上分之旅 #43 计算机科学本质上是数学吗?
️ 本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问. 学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越 ...
- Win10 误删winsock注册表修复。 winsock.reg
手贱删除了注册表的winsock项, 导致无法上网. 导入后需要重启电脑才能上网, 这个文件是我在别人电脑里导出来的. 下载地址: https://pan.baidu.com/s/1wH8SdeWsx ...
- python判断ip所属地区 python 判断ip 网段
IP地址是互联网中唯一标识一个设备的地址,有时候需要判断一个IP地址所属的地区,这就需要用到IP地址归属查询.本文将介绍Python如何通过IP地址查询所属地区并展示代码. 一. IP地址归属查询 I ...
- 7.4 通过API枚举进程权限
GetTokenInformation 用于检索进程或线程的令牌(Token)信息.Token是一个数据结构,其包含有关进程或线程的安全上下文,代表当前用户或服务的安全标识符和权限信息.GetToke ...
- Merkle Tree 简介
Merkle 树(Merkle Tree)是一种树状数据结构,通常用于验证大规模数据集的完整性和一致性.它的名字来源于其发明者 Ralph Merkle.Merkle 树在密码学.分布式系统和区块链等 ...
- 用 Dijkstra 算法解决最短路问题
话不多说,先看图 1.1 朴素版的Dijkstra算法 一般用到这个情况稠密图,也就是节点的个数比边的个数少. (稠密图用邻接矩阵存储) #include<cstring> #includ ...
- getc()、getchar()、getch() 和 getche() 的区别
所有这些函数都从输入中读取一个字符并返回一个整数值.返回整数以容纳用于指示失败的特殊值.EOF值通常用于此目的. getc() 它从给定的输入流中读取单个字符,并在成功时返回相应的整数值(通常 ...
- JS异步任务的并行、串行,以及二者结合
让多个异步任务按照我们的想法执行,是开发中常见的需求.今天我们就来捋一下,如何让多个异步任务并行,串行,以及并行串行相结合. 一.并行 并行是使用最多的方式,多个相互间没有依赖关系的异步任务,并行执行 ...