目录
1. 异步
1.1 初始化线程的 4 种方式
1.1.1 继承 Thread
1.1.2 实现 Runnable 接口
1.1.3 实现 Callable 接口 + FutureTask
1.1.4 线程池
1.1.5 以上 4 种方式总结:
1.2 线程池详解
1.2.1 初始化线程池的 2 种方式
1.2.1.1 使用 Executors 创建
1.2.1.2 使用 ThreadPoolExecutor 创建
1.2.1.2.1 ThreadPoolExecutor 线程池的七大参数
1.2.1.2.2 运行流程
1.2.1.2.3 线程池面试题
1.2.2 常见的 4 种线程池
1.2.3 使用线程池的好处
1.3 CompletableFuture异步编排
1.3.1 启动异步任务
1.3.1.1 runAsync没有返回值
1.3.1.2 supplyAsync有返回值
1.3.2 完成回调与异常感知
1.3.2.1 完成回调 whenComplete
1.3.2.2 异常感知 exceptionally
1.3.3 handle 最终处理
1.3.4 线程串行化
1.3.4.1 thenRunAsync
1.3.4.2 thenAcceptAsync
1.3.4.3 thenApplyAsync
1.3.5 两个任务组合 - 都要完成
1.3.5.1 runAfterBothAsync
1.3.5.2 thenAcceptBothAsync
1.3.5.3 thenCombineAsync
1.3.6 两个任务组合 - 一个完成
1.3.6.1 runAfterEitherAsync
1.3.6.2 acceptEitherAsync
1.3.6.3 applyToEitherAsync
1.3.7 多任务组合
1.3.7.1 allOf
1.3.7.2 anyOf
2. 商品详情
2.1 环境搭建
2.1.1 SwitchHosts增加配置
2.1.2 nginx配置
2.1.3 配置网关转发
2.1.4 商品详情页面搭建
2.1.5 模型抽取
2.1.6 商品详情的业务代码
2.1.6.1 controller层
2.1.6.2 service层
2.1.6.3 获取sku的图片信息
2.1.6.4 获取spu的规格参数
2.1.6.5 获取spu的销售属性组合
2.1.7 详情页渲染【P207-P209】
2.1.8 异步编排优化
2.1.8.1 自定义线程池
2.1.8.1.1 引入依赖
2.1.8.1.2 线程池属性配置类
2.1.8.1.3 配置文件中配置
2.1.8.1.4 自定义线程池
2.1.8.2 异步编排优化商品详情业务
2.1.8.2.1 controller层
2.1.8.2.2 service层
1. 异步
1.1 初始化线程的 4 种方式
1.1.1 继承 Thread
public class ThreadTest {
public static void main(String[] args) {
System.out.println("main...start...");
// 1. 继承Thread类
Thread01 thread01 = new Thread01();
thread01.start(); // 启动线程
System.out.println("main...end...");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
}
测试结果:
1.1.2 实现 Runnable 接口
public class ThreadTest {
public static void main(String[] args) {
System.out.println("main...start...");
// 1. 继承Thread类
//Thread01 thread01 = new Thread01();
//thread01.start(); // 启动线程
// 2. 实现Runnable接口
Runnable01 runnable01 = new Runnable01();
new Thread(runnable01).start();
System.out.println("main...end...");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
}
1.1.3 实现 Callable 接口 + FutureTask
FutureTask的get()方法,阻塞式等待整个线程的执行完成,获取线程执行返回结果。
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
// 1. 继承Thread类
//Thread01 thread01 = new Thread01();
//thread01.start(); // 启动线程
// 2. 实现Runnable接口
//Runnable01 runnable01 = new Runnable01();
//new Thread(runnable01).start();
// 3. 实现Callable接口+FutureTask (可以拿到返回结果,可以处理异常)
Callable01 callable01 = new Callable01();
FutureTask futureTask = new FutureTask<>(callable01);
new Thread(futureTask).start();
// 阻塞式等待整个线程执行完成,获取线程执行返回结果
Integer integer = futureTask.get();
System.out.println("main...end..."+integer );
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
public static class Callable01 implements Callable{
@Override
public Integer call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
return i;
}
}
}
注意:
Future可以获取异步执行结果;
FutureTask继承了Runnable接口。
1.1.4 线程池
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
// 1. 继承Thread类
//Thread01 thread01 = new Thread01();
//thread01.start(); // 启动线程
// 2. 实现Runnable接口
//Runnable01 runnable01 = new Runnable01();
//new Thread(runnable01).start();
// 3. 实现Callable接口+FutureTask (可以拿到返回结果,可以处理异常)
//Callable01 callable01 = new Callable01();
//FutureTask futureTask = new FutureTask<>(callable01);
//new Thread(futureTask).start();
阻塞式等待整个线程执行完成,获取线程执行返回结果
//Integer integer = futureTask.get();
// 在以后的业务代码里面,以上三种启动线程的方式都不用,因为频繁的创建和销毁线程耗费系统资源。【将所有的多线程异步任务都交给线程池执行】
//new Thread(()-> System.out.println("hello")).start();
// 4. 线程池 给线程池直接提交业务
// 当前系统中池只有一两个(例如:核心业务/非核心业务),每个异步任务,提交给线程池让它自己去执行就行
executorService.execute(new Runnable01());
System.out.println("main...end...");
}
public static class Thread01 extends Thread{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
public static class Runnable01 implements Runnable{
@Override
public void run() {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
}
}
public static class Callable01 implements Callable{
@Override
public Integer call() throws Exception {
System.out.println("当前线程:"+Thread.currentThread().getId());
int i = 10/2;
System.out.println("运行结果:"+i);
return i;
}
}
}
1.1.5 以上 4 种方式总结:
区别:
1、2不能得到返回值;3可以获取返回值
1、2、3都不能控制资源
4可以控制资源,性能稳定
注意:
1. 在以后的业务代码里面,以上三种启动线程的方式都不用,因为频繁的创建和销毁线程耗费系统资源,可能导致服务器资源耗尽。【将所有的多线程异步任务都交给线程池执行】
2. 当前系统中池只有一两个(例如:核心业务/非核心业务),每个异步任务,提交给线程池让它自己去执行就行
1.2 线程池详解
1.2.1 初始化线程池的 2 种方式
1.2.1.1 使用 Executors 创建
ExecutorService executorService = Executors.newFixedThreadPool(10);
线程池执行任务的两种方式:
executorService.execute(new Runnable01()); Future submit = executorService.submit(new Runnable01());
区别:
execute:参数只能是Runnable,没有返回值。 submit:参数可以是Runnable、Callable,返回值是Future。
1.2.1.2 使用 ThreadPoolExecutor 创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
200,
10,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
1.2.1.2.1 ThreadPoolExecutor 线程池的七大参数
corePoolSize:核心线程数【一直存在除非(allowCoreThreadTimeOut)】,线程池创建好后就准备就绪,等待任务。
maximumPoolSize:最大线程数,控制资源。
keepAliveTime:存活时间。如果当前的线程数大于核心线程数,只要线程空闲大于指定的keepAliveTime,就会释放空闲的线程【maximumPoolSize-corePoolSize】。
unit:存活时间的时间单位。
workQueue:阻塞队列。如果当前对线程的需求超过核心线程数,就会将多余的任务放到阻塞队列。如果有空闲的线程,就会去队列里取出新的任务继续执行。
threadFactory:创建线程的工厂。
handler:拒绝策略。提交给线程池的任务量超过最大线程池大小+队列长度,按照我们指定的拒绝策略拒绝执行任务。
ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务。
ThreadPoolExecutor.DiscardPolicy:不处理新任务,直接丢弃掉。
ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
1.2.1.2.2 运行流程
1、线程池创建,准备好core数量的核心线程,准备接受任务。 2、core 满了, 就将再进来的任务放入阻塞队列中。 空闲的 core 就会自己去阻塞队列获取任务执行。 3、阻塞队列满了, 就直接开新线程执行, 最大只能开到 max 指定的数量 4、如果线程数开到了 max 的数量, 还有新任务进来, 就会使用 RejectedExecutionHandler拒绝任务。 5、max 都执行好了。 Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自 动销毁。 最终保持到 core 大小 。
1.2.1.2.3 线程池面试题
问题:一个线程池 core 7; max 20 , queue: 50, 100 并发进来怎么分配的; 答案:先有 7 个能直接得到执行, 接下来 50 个进入队列排队, 在多开 13 个继续执行。 现在 70 个被安排上了。 剩下 30 个默认拒绝策略。
1.2.2 常见的 4 种线程池
newCachedThreadPool:创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程。
newFixedThreadPool:创建一个定长线程池, 可控制线程最大并发数, 超出的线程会在队列中等待。
newScheduledThreadPool:创建一个定长线程池, 支持定时及周期性任务执行。
newSingleThreadExecutor:创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
1.2.3 使用线程池的好处
降低资源的消耗:通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗。
提高响应速度:因为线程池中的线程数没有超过线程池的最大上限时, 有的线程处于等待分配任务的状态, 当任务来时无需创建新的线程就能执行。
提高线程的可管理性:线程池会根据当前系统特点对池内的线程进行优化处理, 减少创建和销毁线程带来的系统开销。 无限的创建和销毁线程不仅消耗系统资源, 还降低系统的稳定性, 使用线程池进行统一分配。
1.3 CompletableFuture异步编排
在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture, 提供了非常强大的Future 的扩展功能, 可以帮助我们简化异步编程的复杂性, 提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过`get`方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。
CompletableFuture 和 FutureTask 同属于 Future 接口的实现类, 都可以获取线程的执行结果。
1.3.1 启动异步任务
CompletableFuture 提供了四个静态方法来创建一个异步操作。
public static CompletableFuture runAsync(Runnable runnable)
public static CompletableFuture runAsync(Runnable runnable,Executor executor)
public static CompletableFuture supplyAsync(Supplier supplier)
public static CompletableFuture supplyAsync(Supplier supplier,Executor executor)
注意:
(1)runAsync都是没有返回结果的,supplyAsync都是可以获取返回结果的;
(2)可以传入自定义的线程池,否则就用默认的线程池;
(3)Async代表异步方法。
1.3.1.1 runAsync没有返回值
public static void main(String[] args) {
System.out.println("main...start...");
CompletableFuture future = CompletableFuture.runAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
}, executorService);
System.out.println("main...end...");
}
测试结果:
1.3.1.2 supplyAsync有返回值
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}, executorService);
Integer integer = future.get();
System.out.println("main...end..."+integer);
}
测试结果:
1.3.2 完成回调与异常感知
public CompletableFuture whenComplete(BiConsumer action)
public CompletableFuture whenCompleteAsync(BiConsumer action)
public CompletableFuture whenCompleteAsync(BiConsumer action, Executor executor)
public CompletableFuture exceptionally(Function fn)
whenComplete可以处理正常和异常的结果, exceptionally 处理异常情况。
whenComplete 和 whenCompleteAsync 的区别:
whenComplete: 是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池 来进行执行。
注意:方法不以 Async 结尾, 意味着 Action 使用相同的线程执行, 而 Async 可能会使用其他线程执行(如果是使用相同的线程池, 也可能会被同一个线程选中执行)
1.3.2.1 完成回调 whenComplete
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
//CompletableFuture future = CompletableFuture.runAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
//}, executorService);
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 1;
System.out.println("运行结果:" + i);
return i;
}, executorService).whenComplete((result,exception)->{
// 可以感知到异常,但无法修改返回数据
System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
});
//Integer integer = future.get();
System.out.println("main...end...");
}
}
将 int i = 10/1; 改为 int i = 10/0;
1.3.2.2 异常感知 exceptionally
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
//CompletableFuture future = CompletableFuture.runAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
//}, executorService);
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 2;
System.out.println("运行结果:" + i);
return i;
}, executorService).whenComplete((result,exception)->{
// 可以感知到异常,但无法修改返回数据
System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
}).exceptionally(throwable -> {
// 可以感知异常,同时返回默认值
return 10;
});
Integer integer = future.get();
System.out.println("main...end..."+integer);
}
}
将 int i = 10/2; 改为 int i = 10/0;
1.3.3 handle 最终处理
一般用handle,因为whencomplete如果异常不能给定默认返回结果,需要再调用exceptionally,而handle可以。
handle方法作用:获得前一任务的返回值【handleAsync可以是异步执行的】,也可以处理上一任务的异常,调用exceptionally修改前一任务的返回值【只有在异常情况时给一个默认返回值】而handle方法可以简化操作。
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
//CompletableFuture future = CompletableFuture.runAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
//}, executorService);
/**
* 方法执行完成后的感知
*/
//CompletableFuture future = CompletableFuture.supplyAsync(() -> {
// System.out.println("当前线程:" + Thread.currentThread().getId());
// int i = 10 / 2;
// System.out.println("运行结果:" + i);
// return i;
//}, executorService).whenComplete((result,exception)->{
// // 可以感知到异常,但无法修改返回数据
// System.out.println("异步任务完成了...返回值为:"+result+";异常为:"+exception);
//}).exceptionally(throwable -> {
// // 可以感知异常,同时返回默认值
// return 10;
//});
/**
* 方法执行完成后的处理
*/
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 4;
System.out.println("运行结果:" + i);
return i;
}).handle((res, thr) -> {
if (res != null) {
return res * 2;
}
if (thr != null) {
return 10;
}
return 0;
});
// R apply(T t, U u);
Integer integer = future.get();
System.out.println("main...end..."+integer);
}
}
将 int i = 10/4; 改为 int i = 10/0;
1.3.4 线程串行化
public CompletableFuture thenApply(Function fn)
public CompletableFuture thenApplyAsync(Function fn)
public CompletableFuture thenApplyAsync(Function fn, Executor executor)
public CompletableFuture thenAccept(Consumer action)
public CompletableFuture thenAcceptAsync(Consumer action)
public CompletableFuture thenAcceptAsync(Consumer action,Executor executor)
public CompletableFuture thenRun(Runnable action)
public CompletableFuture thenRunAsync(Runnable action)
public CompletableFuture thenRunAsync(Runnable action,Executor executor)
thenApply 方法: 当一个线程依赖另一个线程时, 获取上一个任务返回的结果, 并返回当前 任务的返回值。thenAccept 方法: 消费处理结果。 接收任务的处理结果, 并消费处理, 无返回结果。thenRun 方法: 只要上面的任务执行完成, 就开始执行 thenRun, 只是处理完任务后, 执行thenRun 的后续操作 带有 Async 默认是异步执行的。 同之前。 以上都要前置任务成功完成。Function T : 上一个任务返回结果的类型。U : 当前任务的返回值类型 。
1.3.4.1 thenRunAsync
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 4;
System.out.println("运行结果:" + i);
return i;
}).thenRunAsync(()->{
// 不接收上一步结果,无返回值
System.out.println("任务2启动了...");
},executorService);
System.out.println("main...end...");
}
执行结果:
1.3.4.2 thenAcceptAsync
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 4;
System.out.println("运行结果:" + i);
return i;
}).thenAcceptAsync(res -> {
// thenRunAsync:不接收上一步结果,无返回值
// thenAcceptAsync:接收上一步结果,无返回值 void accept(T t);
System.out.println("任务2启动了..." + res);
}, executorService);
System.out.println("main...end...");
}
执行结果:
1.3.4.3 thenApplyAsync
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
System.out.println("当前线程:" + Thread.currentThread().getId());
int i = 10 / 4;
System.out.println("运行结果:" + i);
return i;
}).thenApplyAsync(res -> {
// thenRunAsync:不接收上一步结果,无返回值
// thenAcceptAsync:接收上一步结果,无返回值 void accept(T t);
// thenApplyAsync:接收上一步结果,有返回值 R apply(T t);
System.out.println("任务2启动了..." + res);
return "Hello" + res;
}, executorService);
System.out.println("main...end..."+future.get());
}
执行结果:
1.3.5 两个任务组合 - 都要完成
public CompletableFuture thenCombine(CompletionStage other,
BiFunction fn)
public CompletableFuture thenCombineAsync(CompletionStage other,
BiFunction fn)
public CompletableFuture thenCombineAsync(CompletionStage other,
BiFunction fn, Executor executor)
public CompletableFuture thenAcceptBoth(CompletionStage other,
BiConsumer action)
public CompletableFuture thenAcceptBothAsync(CompletionStage other,
BiConsumer action)
public CompletableFuture thenAcceptBothAsync(CompletionStage other,
BiConsumer action, Executor executor)
public CompletableFuture runAfterBoth(CompletionStage other,Runnable action)
public CompletableFuture runAfterBothAsync(CompletionStage other,Runnable action)
public CompletableFuture runAfterBothAsync(CompletionStage other,Runnable action,
Executor executor)
两个任务必须都完成, 触发该任务。thenCombine : 组合两个 future, 获取两个 future 的返回结果, 并返回当前任务的返回值thenAcceptBoth : 组合两个 future, 获取两个 future 任务的返回结果, 然后处理任务, 没有 返回值。runAfterBoth : 组合两个 future, 不需要获取 future 的结果, 只需两个 future 处理完任务后, 处理该任务。
1.3.5.1 runAfterBothAsync
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
/**
* 两个任务组合-都要完成
*/
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
future01.runAfterBothAsync(future02,()->{
System.out.println("任务3开始");
},executorService);
System.out.println("main...end...");
}
}
执行结果:
1.3.5.2 thenAcceptBothAsync
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
/**
* 两个任务组合-都要完成
*/
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
//future01.runAfterBothAsync(future02,()->{
// System.out.println("任务3开始");
//},executorService);
future01.thenAcceptBothAsync(future02,(f1,f2)->{
//void accept(T t, U u);
System.out.println("任务3开始。。。之前的结果:"+f1+"->"+f2);
},executorService);
System.out.println("main...end...");
}
}
执行结果:
1.3.5.3 thenCombineAsync
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
/**
* 两个任务组合-都要完成
*/
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
//future01.runAfterBothAsync(future02,()->{
// System.out.println("任务3开始");
//},executorService);
//future01.thenAcceptBothAsync(future02,(f1,f2)->{
// //void accept(T t, U u);
// System.out.println("任务3开始。。。之前的结果:"+f1+"->"+f2);
//},executorService);
CompletableFuture future = future01.thenCombineAsync(future02, (f1, f2) -> {
return f1 + "->" + f2 + "->相柳"; // R apply(T t, U u);
}, executorService);
System.out.println("main...end..."+future.get());
}
}
执行结果:
1.3.6 两个任务组合 - 一个完成
public CompletableFuture applyToEither(CompletionStage other, Function fn)
public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn)
public CompletableFuture applyToEitherAsync(CompletionStage other, Function fn,Executor executor)
public CompletableFuture acceptEither(CompletionStage other, Consumer action)
public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action)
public CompletableFuture acceptEitherAsync(CompletionStage other, Consumer action,Executor executor)
public CompletableFuture runAfterEither(CompletionStage other,Runnable action)
public CompletableFuture runAfterEitherAsync(CompletionStage other,Runnable action)
public CompletableFuture runAfterEitherAsync(CompletionStage other,Runnable action,Executor executor)
当两个任务中, 任意一个 future 任务完成的时候, 执行任务。applyToEither : 两个任务有一个执行完成, 获取它的返回值, 处理任务并有新的返回值。acceptEither : 两个任务有一个执行完成, 获取它的返回值, 处理任务, 没有新的返回值。runAfterEither : 两个任务有一个执行完成, 不需要获取 future 的结果, 处理任务, 也没有返 回值。
1.3.6.1 runAfterEitherAsync
线程1执行完成,线程2睡了3秒,达成runAfterEitherAsync执行条件
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
/**
* 两个任务,只要有一个完成,我们就执行任务3
*/
future01.runAfterEitherAsync(future02,()->{
System.out.println("任务3开始。。。之前的结果:");
},executorService);
System.out.println("main...end...");
}
}
执行结果:
1.3.6.2 acceptEitherAsync
两个任务的返回值类型要一致,这里全改为Object
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
/**
* 两个任务,只要有一个完成,我们就执行任务3
*/
//future01.runAfterEitherAsync(future02,()->{
// System.out.println("任务3开始。。。之前的结果:");
//},executorService);
future01.acceptEitherAsync(future02,res->{
System.out.println("任务3开始。。。之前的结果:"+res);
},executorService);
System.out.println("main...end...");
}
}
执行结果:
1.3.6.3 applyToEitherAsync
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture future01 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务1线程:"+Thread.currentThread().getId());
int i = 10/4;
System.out.println("任务1结束。。。");
return i;
}, executorService);
CompletableFuture future02 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2线程:"+Thread.currentThread().getId());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务2结束。。。");
return "Hello";
}, executorService);
/**
* 两个任务,只要有一个完成,我们就执行任务3
*/
//future01.runAfterEitherAsync(future02,()->{
// System.out.println("任务3开始。。。之前的结果:");
//},executorService);
//future01.acceptEitherAsync(future02,res->{
// System.out.println("任务3开始。。。之前的结果:"+res);
//},executorService);
CompletableFuture future = future01.applyToEitherAsync(future02, res -> {
System.out.println("任务3开始。。。之前的结果:"+res);
return res.toString() + "九头蛇";
}, executorService);
System.out.println("main...end..."+future.get());
}
}
执行结果:
1.3.7 多任务组合
public static CompletableFuture allOf(CompletableFuture... cfs)
public static CompletableFuture anyOf(CompletableFuture... cfs)
allOf : 等待所有任务完成anyOf : 只要有一个任务完成
1.3.7.1 allOf
让查询商品介绍睡3秒钟,便于观察。
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture futureImg = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的图片信息");
return "hello.jpg";
}, executorService);
CompletableFuture futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的属性");
return "白色+256G";
}, executorService);
CompletableFuture futureDesc = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("查询商品介绍");
return "小米";
}, executorService);
CompletableFuture allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
allOf.get();// 等待所有任务完成
System.out.println("main...end..."+futureImg.get()+"->"+futureAttr.get()+">"+futureDesc.get());
}
}
执行结果:
1.3.7.2 anyOf
public class ThreadTest {
public static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("main...start...");
CompletableFuture futureImg = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的图片信息");
return "hello.jpg";
}, executorService);
CompletableFuture futureAttr = CompletableFuture.supplyAsync(() -> {
System.out.println("查询商品的属性");
return "白色+256G";
}, executorService);
CompletableFuture futureDesc = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("查询商品介绍");
return "小米";
}, executorService);
//CompletableFuture allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
//allOf.get();// 等待所有任务完成
//System.out.println("main...end..."+futureImg.get()+"->"+futureAttr.get()+">"+futureDesc.get());
CompletableFuture anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
System.out.println("main...end..."+anyOf.get());
}
}
执行结果:
2. 商品详情
2.1 环境搭建
2.1.1 SwitchHosts增加配置
添加商品详情的域名与ip映射:172.1.11.10 item.gulimall.com
2.1.2 nginx配置
配置域名 item.gulimall.com,这里之前应该配置过,如下:
server_name gulimall.com *.gulimall.com;
# 打开nginx配置文件
vi /root/docker/nginx/conf/conf.d/gulimall.conf
2.1.3 配置网关转发
将商品详情域名(item.gulimall.com)访问转发的商品服务。
- id: gulimall_host_route
uri: lb://gulimall-product
predicates:
# 由以下的主机域名访问转发到商品服务
- Host=gulimall.com,item.gulimall.com
2.1.4 商品详情页面搭建
1. 将shangpinxiangqing.html复制到gulimall-product/src/main/resources/templates,并改名为item.html;
2. 将静态资源放到nginx的/root/docker/nginx/html/static/item/路径下;
3. 将item.html页面的href和src以/static/item/开头。
4. 在gulimall-product/src/main/java/com/wen/gulimall/product/web/下编写ItemController.java;
5. 启动网关服务、商品服务、搜索服务,进行测试:
1)在搜索服务点击某个商品发现无法跳转至商品详情服务,修改搜索服务list.html,如下:
通过右键商品审查元素,发现需要修改class="da"的图片跳转路径,如下:
@Controller
public class ItemController {
@GetMapping("/{skuId}.html")
public String item(@PathVariable Long skuId){
System.out.println("准备查询:"+skuId+"的详情!");
return "item";
}
}
2.1.5 模型抽取
封装页面需要的详情数据模型,如下:
@Data
public class SkuItemVo {
// 获取sku的基本信息 pms_sku_info
private SkuInfoEntity info;
// 获取sku的图片信息 pms_sku_images
private List images;
// 获取spu的销售属性组合
private List saleAttr;
// 获取spu的介绍
private SpuInfoDescEntity desc;
// 获取spu的规格参数信息
private List groupAttrs;
}
@ToString
@Data
public class SpuItemAttrGroupVo {
private String groupName;
private List attrs;
}
// 之前代码中存在
@Data
public class Attr {
private Long attrId;
private String attrName;
private String attrValue;
}
@ToString
@Data
public class SkuItemSaleAttrVo {
private Long attrId;
private String attrName;
private List attrValues;
}
@Data
public class AttrValueAndWithSkuIdVo {
// 属性值
private String attrValue;
// 属性编号,属性名,属性值对应的所有skuId
private String skuIds;
}
2.1.6 商品详情的业务代码
2.1.6.1 controller层
@Controller
public class ItemController {
@Resource
private SkuInfoService skuInfoService;
@GetMapping("/{skuId}.html")
public String item(@PathVariable Long skuId, Model model){
// 获取sku的基本信息 pms_sku_info
// 获取sku的图片信息 pms_sku_images
// 获取spu的销售属性组合
// 获取spu的介绍
// 获取spu的规格参数信息
SkuItemVo vo = skuInfoService.item(skuId);
model.addAttribute("item",vo);
return "item";
}
}
2.1.6.2 service层
public interface SkuInfoService extends IService {
...
SkuItemVo item(Long skuId);
}
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl implements SkuInfoService {
@Resource
private SkuImagesService skuImagesService;
@Resource
private SpuInfoDescService spuInfoDescService;
@Resource
private AttrGroupService attrGroupService;
@Resource
private SkuSaleAttrValueService skuSaleAttrValueService;
...
@Override
public SkuItemVo item(Long skuId) {
SkuItemVo skuItemVo = new SkuItemVo();
// 获取sku的基本信息 pms_sku_info
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
Long spuId = info.getSpuId();
Long catalogId = info.getCatalogId();
// 获取sku的图片信息 pms_sku_images
List images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
// 获取spu的销售属性组合
List saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId);
skuItemVo.setSaleAttr(saleAttrVos);
// 获取spu的介绍
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
skuItemVo.setDesc(spuInfoDescEntity);
// 获取spu的规格参数信息
List attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
skuItemVo.setGroupAttrs(attrGroupVos);
return skuItemVo;
}
}
2.1.6.3 获取sku的图片信息
public interface SkuImagesService extends IService {
...
List getImagesBySkuId(Long skuId);
}
@Service("skuImagesService")
public class SkuImagesServiceImpl extends ServiceImpl implements SkuImagesService {
...
@Override
public List getImagesBySkuId(Long skuId) {
List imagesEntities = this.baseMapper.selectList(new QueryWrapper().eq("sku_id", skuId));
return imagesEntities;
}
}
2.1.6.4 获取spu的规格参数
public interface AttrGroupService extends IService {
...
List getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId);
}
@Service("attrGroupService")
public class AttrGroupServiceImpl extends ServiceImpl implements AttrGroupService {
...
@Override
public List getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {
List vos = this.baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
return vos;
}
}
@Mapper
public interface AttrGroupDao extends BaseMapper {
List getAttrGroupWithAttrsBySpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId);
}
SELECT
pav.spu_id,
ag.attr_group_id,
ag.attr_group_name groupName,
attr.attr_name attrName,
pav.attr_value attrValue
FROM pms_attr_group ag LEFT JOIN pms_attr_attrgroup_relation aar ON (ag.attr_group_id=aar.attr_group_id)
LEFT JOIN pms_attr attr ON (aar.attr_id=attr.attr_id)
LEFT JOIN pms_product_attr_value pav ON (attr.attr_id = pav.attr_id)
WHERE ag.catelog_id = #{catalogId} AND pav.spu_id = #{spuId}
2.1.6.5 获取spu的销售属性组合
public interface SkuSaleAttrValueService extends IService {
...
List getSaleAttrsBySpuId(Long spuId);
}
@Service("skuSaleAttrValueService")
public class SkuSaleAttrValueServiceImpl extends ServiceImpl implements SkuSaleAttrValueService {
...
@Override
public List getSaleAttrsBySpuId(Long spuId) {
List vos = this.baseMapper.getSaleAttrsBySpuId(spuId);
return vos;
}
}
@Mapper
public interface SkuSaleAttrValueDao extends BaseMapper {
List getSaleAttrsBySpuId(@Param("spuId") Long spuId);
}
SELECT
ssav.attr_id,
ssav.attr_name,
ssav.attr_value,
GROUP_CONCAT(DISTINCT info.sku_id) sku_ids
FROM pms_sku_info info LEFT JOIN pms_sku_sale_attr_value ssav ON info.sku_id=ssav.sku_id
WHERE info.spu_id=#{spuId}
GROUP BY ssav.attr_id,ssav.attr_name,ssav.attr_value
2.1.7 详情页渲染【P207-P209】
>
>
>
华为 (HUAWEI)
华为(huawei)
小米(xiaomi)
APPle
魅族(meizu)
锤子
>
华为Mate 10
关注
对比
华为 HUAWEI Mate 10 6GB+128GB 亮黑色 移动联通电信4G手机 双卡双待
预订用户预计11月30日左右陆续发货!麒麟970芯片!AI智能拍照!
配送至
北京朝阳区管庄
无货 , 此商品暂时售完
分享
立即预约
厂家服务
本产品全国联保,享受三包服务,质保期为:一年保
如因质量问题或故障,凭厂商维修中心或特约维修点的质量检测证明,享受7日内退货,15日内换货,15日以上在保质期内享受免费保修等安保服务!
(注:如厂家在商品介绍中有售后保障的说明,则此商品按照厂家说明执行售后保障服务。)您可以查询本品牌在各地售后服务中心的练习方式
请点击这儿查询...
京东承诺
本产品全国联保,享受三包服务,质保期为:一年保
如因质量问题或故障,凭厂商维修中心或特约维修点的质量检测证明,享受7日内退货,15日内换货,15日以上在保质期内享受免费保修等安保服务!
(注:如厂家在商品介绍中有售后保障的说明,则此商品按照厂家说明执行售后保障服务。)您可以查询本品牌在各地售后服务中心的练习方式
请点击这儿查询...
正品行货
京东商城向您保证所售商品均为正品行货,京东自营商品开具机打发票或电子发票。
全国联保
凭质保证书及京东商城发票,可享受全国联保服务(奢侈品、钟表除外;奢侈品、钟表由京东联系保修,享受法定三包售后服务),与您亲临商场选购的商品享受相同的质量保证。京东商城还为您提供具有竞争力的商品价格和运费政策,请您放心购买!
注:因厂家会在没有任何提前通知的情况下更改产品包装、产地或者一些附件,本司不能确保客户收到的货物与商城图片、产地、附件说明完全一致。只能确保为原厂正货!并且保证与当时市场上同样主流新品一致。若本商城没有及时更新,请大家谅解!
权利声明:
京东上的所有商品信息、客户评价、商品咨询、网友讨论等内容,是京东重要的经营资源,未经许可,禁止非法转载使用。
注 :本站商品信息均来自于合作方,其真实性、准确性和合法性由信息拥有者(合作方)负责。本站不提供任何保证,并不承担任何法律责任。
价格说明:
京东价 :京东价为商品的销售价,是您最终决定是否购买商品的依据。
划线价 :商品展示的划横线价格为参考价,该价格可能是品牌专柜标价、商品吊牌价或由品牌供应商提供的正品零售价(如厂商指导价、建议零售价等)或该商品在京东平台上曾经展示过的销售价;由于地区、时间的差异性和市场行情波动,品牌专柜标价、商品吊牌价等可能会与您购物时展示的不一致,该价格仅供您参考。
折扣 :如无特殊说明,折扣指销售商在原价、或划线价(如品牌专柜标价、商品吊牌价、厂商指导价、厂商建议零售价)等某一价格基础上计算出的优惠比例或优惠金额;如有疑问,您可在购买前联系销售商进行咨询。
异常问题 :商品促销信息以商品详情页“促销”栏中的信息为准;商品的具体售价以订单结算页价格为准;如您发现活动商品售价或促销信息有异常,建议购买前先联系销售商咨询。
商品评价
购物车还没有商品,赶紧选购吧!
2.1.8 异步编排优化
所有的操作针对于商品服务gulimall-product。
2.1.8.1 自定义线程池
2.1.8.1.1 引入依赖
spring-boot-configuration-processor作用:你自己创建的配置类生成元数据信息,这样就能在你自己的配置文件中显示出来非常的方便。【可以不加】
org.springframework.boot
spring-boot-configuration-processor
true
引入以上依赖,配置文件中会显示:
2.1.8.1.2 线程池属性配置类
gulimall-product/src/main/java/com/wen/gulimall/product/config/ThreadPoolConfigProperties.java
@Data
@Component
@ConfigurationProperties(prefix = "gulimall.thread")
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
2.1.8.1.3 配置文件中配置
gulimall:
thread:
core-size: 20
max-size: 200
keep-alive-time: 10
2.1.8.1.4 自定义线程池
gulimall-product/src/main/java/com/wen/gulimall/product/config/MyThreadConfig.java
/**
* @description 线程池配置
* 注意:线程池属性类ThreadPoolConfigProperties已经使用了@Component放入容器中,
* 不需要在使用@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
*/
//@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties poolProperties){
return new ThreadPoolExecutor(poolProperties.getCoreSize(),
poolProperties.getMaxSize(),
poolProperties.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
}
2.1.8.2 异步编排优化商品详情业务
2.1.8.2.1 controller层
@Controller
public class ItemController {
@Resource
private SkuInfoService skuInfoService;
@GetMapping("/{skuId}.html")
public String item(@PathVariable Long skuId, Model model){
// 获取sku的基本信息 pms_sku_info
// 获取sku的图片信息 pms_sku_images
// 获取spu的销售属性组合
// 获取spu的介绍
// 获取spu的规格参数信息
SkuItemVo vo = null;
try {
vo = skuInfoService.item(skuId);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
model.addAttribute("item",vo);
return "item";
}
}
2.1.8.2.2 service层
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl implements SkuInfoService {
@Resource
private SkuImagesService skuImagesService;
@Resource
private SpuInfoDescService spuInfoDescService;
@Resource
private AttrGroupService attrGroupService;
@Resource
private SkuSaleAttrValueService skuSaleAttrValueService;
@Resource
private ThreadPoolExecutor threadPoolExecutor;
...
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
// 1.获取sku的基本信息 pms_sku_info
CompletableFuture infoFuture = CompletableFuture.supplyAsync(() -> {
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, threadPoolExecutor);
CompletableFuture saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
// 3.获取spu的销售属性组合
List saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, threadPoolExecutor);
CompletableFuture descFuture = infoFuture.thenAcceptAsync(res -> {
// 4.获取spu的介绍
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfoDescEntity);
}, threadPoolExecutor);
CompletableFuture baseAttrFuture = infoFuture.thenAcceptAsync(res -> {
// 5.获取spu的规格参数信息
List attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroupVos);
}, threadPoolExecutor);
CompletableFuture imageFuture = CompletableFuture.runAsync(() -> {
// 2.获取sku的图片信息 pms_sku_images
List images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, threadPoolExecutor);
// 等待所有任务都完成,不用写infoFuture,因为saleAttrFuture/descFuture/baseAttrFuture他们依赖infoFuture完成的结果
CompletableFuture.anyOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
return skuItemVo;
}
}
你可能感兴趣的:(java,android,开发语言)
springboot在线票务预订平台(特麦网)设计与实现
pingfan_yu
SpringBoot实战案例 计算机毕业设计实战案例 spring boot 后端 java
开发技术简介开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9浏览器:谷歌浏览器后台路径地址:localhost:8080/项目名称/admin/dist/index.html前台路径地址:loca
Java设计模式(简易版)
JIU_WW
java 设计模式 javascript 开发语言
第1章单例模式1.1单例模式1.1.1什么是单例模式所谓单例,指的就是单实例,有且仅有一个类的实例,该类提供了一个全局访问点供外部获取该实例,这个单例不应该由人来控制,而由该类负责创建自己的对象,同时确保只有单个对象被创建。在创建实例时,检查系统是否已经存在该单例,如果存在则返回该实例,否则创建一个新实例。总结:单例类只能有一个实例,单例类必须自己创建自己的唯一实例。1.1.2为什么要用单例模式单
广州游戏公司4399秋季招聘火热进行中可查询流程
huaxinjiayou
java
新凯来入职体检被卡取消录用面试多起来了byd是真抽象啊,没笔没面直接录取了金蝶第二批组内直招前端开发实习生-最快可当天约面比亚迪两院不卡本2秋招暂时结束,学会和自己和解应届生基本都沦为各公司的免税工具了秋招不要焦虑,机会就在某一瞬间同程一面面试多起来了同程旅行java开发一面凉经byd是真抽象啊,没笔没面直接录取了同程旅行同程旅行一面数字马力(郑州)10/12面经同程一面写面经攒人品-数字马力后端
Java——接口
六七_Shmily
java java python 开发语言
在Java中,接口(Interface)是一种特殊的引用类型,用于定义一组方法的规范(即方法签名),而不包含方法的实现。接口是Java实现多态和抽象的重要机制之一。以下是关于Java接口的详细说明:1.接口的定义接口使用interface关键字定义。它可以包含:抽象方法:没有方法体的方法。默认方法(Java8+):使用default关键字定义,可以有方法体。静态方法(Java8+):使用stati
每日一题之(多态 )三 学习java
座山雕~
学习 java
重要的几句话:(1)一个对象的编译类型和运行类型可以不一致如:Dog类是Anlimal的子类,Animalanimal=newDog();(2)编译类型在定义对象时,就确定了,不能改变。如:一旦创建,Animal就是编译类型,不会在改变Animalanimal=newDog();(3)运行类型是可以改变的如:此时,编译类型仍然是Animal,运行类型就从Dog变成Cat。Animalanimal=
jar、war、pom
JIU_WW
jar java
1.jar定义与用途用途:默认打包类型,生成JAR文件(JavaArchive),适用于普通Java应用或库。场景:开发工具类库(如commons-lang.jar)。构建可执行应用(通过java-jar运行)。关键行为构建流程:执行mvnpackage后,生成target/.jar。包含编译后的.class文件、资源文件(如.properties)和META-INF/MANIFEST.MF。依赖
【大厂面试题】大疆,最新面试题
扫地僧009
互联网大厂面试题 java 面试 spring
大疆一面(50分钟)开场三连问自我介绍?(自我介绍重点引导面试官想问你的技术栈和项目)对大疆有什么了解?(大家面试的时候,最好提前了解一些公司的业务,不然没话可说就尴尬了呢)为什么选择这个岗位?(先从工作/项目匹配度角度出发,然后再说公司和个人兴趣等原因)Java提供了有哪些支持互斥的工具?工具适用场景特点synchronized简单同步或低竞争环境代码简单,性能经过JVM优化(偏向锁、轻量级锁)
协程的基本实现原理详解以及在java中的使用
一个儒雅随和的男子
多线程 java 开发语言
前言接下来是Java中的协程实现。JDK17引入了虚拟线程(Fiber),属于用户态线程,由JVM管理。本文将深度讨论了第三方库如Quasar,Quasar通过字节码增强实现协程,使用Fiber类。并涉及到Kilim等库也是通过字节码生成实现的,以及Kotlin协程的代码示例,通过suspend函数和CoroutineScope管理。详细讲解了NtyCo库的实现,包括上下文切换和调度器。需要注意,
Android蓝牙通讯开发指南
缘来的精彩
android Bluetooth
在Android平台上进行蓝牙通讯开发涉及多个步骤,包括权限申请、设备发现、连接、数据传输等。以下是开发蓝牙通讯的基本流程和关键点:1.权限申请首先,在AndroidManifest.xml文件中声明蓝牙相关的权限:运行HTML从Android6.0(API23)开始,访问位置信息需要动态申请权限:if(ContextCompat.checkSelfPermission(this,Manifest
Java学习笔记——单元测试,面试必备
m0_64867152
程序员 面试 java 后端
assertEquals(“这是错误信息2”,12,m.getArea(3,4));}}@Test注解的方法每个@Test对应一个方法,这个方法会被识别为一个测试方法一个测试类里面可以有多个@Test,但是每个@Test对应的测试方法只会被执行一次通常我们会在@Test测试方法中使用assertEquals断言语句,来判断方法是否能够正常运行并且输出我们希望的结果assertEquals(“错误信
2025年投身Java培训的可行性分析——基于计算机科学与技术专业的视角
车载诊断工程师-小白
文章 java 开发语言
2025年投身Java培训的可行性分析——基于计算机科学与技术专业的视角2025年投身Java培训的可行性分析引言随着信息技术的快速发展,编程语言的需求和应用领域也在不断变化。Java作为一种广泛应用的编程语言,在企业级应用开发、安卓应用开发、大数据处理等多个领域中占据重要位置。对于计算机科学与技术专业的学生或从业者而言,掌握Java不仅有助于增强个人竞争力,还能拓宽职业发展路径。本文将从市场需求
CellWriteHandler is an interface in EasyExcel, a popular Java library for reading and writing Excel
hshpy
excel
CellWriteHandlerisaninterfaceinEasyExcel,apopularJavalibraryforreadingandwritingExcelfiles.ItisusedtocustomizehowdataiswrittentocellsduringtheExcelexportprocess.PurposeItallowsdeveloperstomodifycellva
华为od机试 - 服务器广播、需要广播的服务器数量(Java & Python& JS & C++ & C )
算法大师
最新华为OD机试 华为 服务器 c++ 华为od机试 javascript 华为OD机试E卷 python
最新华为OD机试真题目录:点击查看目录华为OD面试真题精选:点击立即查看题目描述服务器连接方式包括直接相连,间接连接。A和B直接连接,B和C直接连接,则A和C间接连接。直接连接和间接连接都可以发送广播。给出一个N*N数组,代表N个服务器,matrix[i][j]==1,则代表i和j直接连接;不等于1时,代表i和j不直接连接。matrix[i][i]==1,即
TypeScript与JavaScript的区别
本文我们要聊一聊TypeScript和JavaScript之间的区别。可能我们已经注意到,TypeScript是JavaScript的超集,那么它到底比JavaScript多了些什么?为什么我们要选择TypeScript,而不仅仅是写普通的JavaScript呢?本文我们就一起看看TypeScript和JavaScript的核心差异,让你对它们有个更加清晰的认识。3.1什么是TypeScript与
教妹学Java(三十八):instanceof 操作符的用法
工程师_axI
java python 开发语言
System.out.println(simpleinstanceofSimple);}}在上面这个例子中,我们使用instanceof判断simple这个引用变量是不是Simple类。输出结果如下所示:true一个子类的对象既是子类也是父类,来看下面这个示例:/**@author微信搜「沉默王二」,回复关键字PDF*/classAnimal{}publicclassDogextendsAnima
es部署报错找不到tools.jar
Gungnirss
elasticsearch jar 大数据
网上看了很多解决方法都不行,换版本,甚至用es内置的jdk都没解决问题。原因:系统在运行时会去环境变量里找JAVA_HOME,来找到JDK运行JVM,而JVM在运行时会根据classpath的设置来加载类和资源。此时如果你的classpath里面写了,需要加载tools.jar,而在Java高版本中(实测jdk21没有)是没有tools.jar的,因此会导致报错。解决办法:编辑环境变量-系统变量-
【Java 常用注解学习笔记1】——Java 常用注解全解析:从基础到实战
猿享天开
Java开发从入门到精通 java 学习 笔记
Java常用注解全解析:从基础到实战一、引言Java注解(Annotation)是现代化开发中提升代码简洁性、可维护性和与框架集成的重要工具。本文系统化梳理主流框架(Spring、MyBatis-Plus、Swagger等)的核心注解,通过分类解析、代码示例与最佳实践,帮助开发者精准掌握注解的应用场景与技巧。二、Lombok注解1.代码简化注解注解作用示例注意事项@Data生成getter/set
Java 登录框架
zerolbsony
spring boot java 后端
Java框架中常用的几种成熟的token生成框架对比-白露~-博客园SpringBoot整合sa-token,jwt登录及拦截器鉴权Demo_只有在集成sa-token-jwt插件后才可以使用extra扩展参数-CSDN博客推荐一款轻量级权限认证框架Sa-Token,集成JWT和Redis轻松实现认证鉴权_sa-tokenjwt-CSDN博客SpringBoot整合sa-token,jwt登录及拦
Java String 类
今天你慧了码码码码码码码码码码
JavaSE基础 java python 开发语言
JavaString类常用方法详解在Java编程里,字符串操作十分常见,而String类作为Java标准库的核心类,用于表示不可变的字符序列。任何对字符串的修改操作都会返回一个新的字符串对象,不会改变原始字符串。本文将详细介绍String类的常用方法,并结合示例代码帮助理解。一、String对象的创建String对象的创建主要有以下两种方式:1.直接赋值这是最常见的创建字符串的方式,使用双引号将一
深度解析Android平台Camera2与大牛直播SDK的对接及应用
音视频牛哥
GB28181接入 RTMP推流 轻量级RTSP服务 音视频 实时音视频 gb28181 camera2相对camera camera2 rtrmp camera2 rtsp camera2直播
在移动互联网时代,实时视频采集、编码、推流和设备接入等技术在直播、安防监控等领域得到了广泛应用。本文将深入探讨如何在Android平台上利用Camera2API采集摄像头数据,并结合大牛直播SDK的RTMP推送模块、轻量级RTSP服务模块以及GB28181设备接入模块,实现高效稳定的视频处理和设备接入功能。一、Camera2API的优势与应用1.Camera2API的优势Camera2API是An
Java 基础语法——关键字、变量和运算符
小志开发
JAVA java 开发语言
在Java编程中,掌握基础知识是理解更复杂功能的前提。本篇文章将详细讲解Java中的**关键字(keyword)、变量(variable)以及运算符(operator)**等基本概念,并通过示例代码帮助你理解和应用它们。一、关键字(keyword)在Java中,关键字用于定义类、方法、字段和其他语句结构。这些reservedwords是特殊的字符串,不能用作变量名或其他标识符。1.类和接口关键字p
A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction 报错
Dic-
# Android Studio IDE 单元测试 android studio android ide 编译报错 单元测试 JUnit AndroidJUnit
问题AndroidStudio编译应用报错:>Task:mergeDebugJavaResourceFAILEDExecutionfailedfortask':mergeDebugJavaResource'.>Afailureoccurredwhileexecutingcom.android.build.gradle.internal.tasks.MergeJavaResWorkAction>6f
解析 uni-app 小程序分包策略:合理优化包体积分布
前端梦工厂+
uni-app 前端开发 前端 uni-app 小程序
引言微信小程序的流行使得越来越多的开发者投入到小程序的开发中。但是,随着项目规模的增大,小程序的性能也会面临一些挑战。其中,小程序分包策略是提升性能的重要一环。同时,uni-app的流行也使众多的移动端开发者选择使用uni-app框架来开发移动端应用,因为开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、
Flutter-Android编译报错与解决方案汇总
ShawnRacine
flutter android
报错一:java.lang.NullPointerException:Cannotinvoke“String.length()”because“”isnull解决方案:IsolvedthisproblembyupgradingAndroidGradlePlugintoversion8.3.1.YoucanuseTools->AGPUpgradeAssistanttoupgradeAndroidGr
Promise_链式调用(解决回调函数地狱)
还是鼠鼠
javascript ajax 前端 vscode html5
目录介绍代码结构代码index.html(HTML部分)运行结果代码分析总结介绍本示例展示了如何使用JavaScript中的Promise对象解决传统回调函数嵌套(即“回调地狱”)的问题。通过链式调用的方式,使得多个异步操作变得更加线性和易于维护。代码演示了如何利用Promise按顺序获取省、市、区的信息,并将它们依次拼接成完整的地址。代码结构HTML部分:HTML代码主要提供一个简单的框架,包含
部署若依微服务遇到的坑
高树悲风
微服务 架构 云原生
1、用Windows部署nacosjava.nio.charset.MalformedInputException:Inputlength=1或Inputlength=2-CSDN博客我的解决方案:遇到这个问题修改也yml文件上的中文没用使用第二种File->Settings->FileEncodings
deepin Java开发环境搭建和主力使用体验(2021.02更)
我是你的橙子啊
软件安装类 推荐 deepin java intellij idea mysql linux
前言:之所以想体验一下deepin有2点原因1.美观(是的,没错,就是冲着好看去的)2.流畅(主要是一些编程软件比win下反应更快,体验更好)成果:1.UI整体风格我觉得OK(基本不用动手美化,原生的UI就够了)2.丝滑有些许夸大,流畅是一点不假(针对我常用的一些软件而言,ex,IDEA)so,符合预期,总体感觉良好,打算作为主力使用一段时间我的桌面时尚模式更好看一些,但是任务栏占的地方比较大,配
华为OD机试2024年E卷-TLV解码[100分]( Java | Python3 | C++ | C语言 | JsNode | Go )实现100%通过率
梅花C
华为OD题库 华为od
题目描述TLV编码是按[TagLengthValue]格式进行编码的,一段码流中的信元用Tag标识,Tag在码流中唯一不重复,Length表示信元Value的长度,Value表示信元的值。码流以某信元的Tag开头,Tag固定占一个字节,Length固定占两个字节,字节序为小端序。现给定TLV格式编码的码流,以及需要解码的信元Tag,请输出该信元的Value。输入码流的16进制字符中,不包括小写字母
MAC电脑 初始化 开发环境(Java + Node)
Kevin_K2
macos java 开发语言
1.后端1.1Java下载地址https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html1.2Maven安装https://archive.apache.org/dist/maven/maven-3/1.3maven环境变量exportMAVEN_HOME=/Users/kevin/Soft/a
关于在mac中配置Java系统环境变量
我要最优解
macos java flutter
引言在macOS上开发Java或Flutter应用时,正确配置环境变量是至关重要的。环境变量不仅能让系统找到开发工具的位置,还能简化命令行操作。本文将手把手教你从零开始安装JavaSDK,并详细配置环境变量,涵盖常见问题解决和优化技巧。在macOS系统中配置Java环境变量的详细步骤如下:一、配置Java环境变量1.安装JavaJDKmacOS默认可能未安装JDK(或版本较旧),以下是两种安装方式
java杨辉三角
3213213333332132
java基础
package com.algorithm;
/**
* @Description 杨辉三角
* @author FuJianyong
* 2015-1-22上午10:10:59
*/
public class YangHui {
public static void main(String[] args) {
//初始化二维数组长度
int[][] y
《大话重构》之大布局的辛酸历史
白糖_
重构
《大话重构》中提到“大布局你伤不起”,如果企图重构一个陈旧的大型系统是有非常大的风险,重构不是想象中那么简单。我目前所在公司正好对产品做了一次“大布局重构”,下面我就分享这个“大布局”项目经验给大家。
背景
公司专注于企业级管理产品软件,企业有大中小之分,在2000年初公司用JSP/Servlet开发了一套针对中
电驴链接在线视频播放源码
dubinwei
源码 电驴 播放器 视频 ed2k
本项目是个搜索电驴(ed2k)链接的应用,借助于磁力视频播放器(官网:
http://loveandroid.duapp.com/ 开放平台),可以实现在线播放视频,也可以用迅雷或者其他下载工具下载。
项目源码:
http://git.oschina.net/svo/Emule,动态更新。也可从附件中下载。
项目源码依赖于两个库项目,库项目一链接:
http://git.oschina.
Javascript中函数的toString()方法
周凡杨
JavaScript js toString function object
简述
The toString() method returns a string representing the source code of the function.
简译之,Javascript的toString()方法返回一个代表函数源代码的字符串。
句法
function.
struts处理自定义异常
g21121
struts
很多时候我们会用到自定义异常来表示特定的错误情况,自定义异常比较简单,只要分清是运行时异常还是非运行时异常即可,运行时异常不需要捕获,继承自RuntimeException,是由容器自己抛出,例如空指针异常。
非运行时异常继承自Exception,在抛出后需要捕获,例如文件未找到异常。
此处我们用的是非运行时异常,首先定义一个异常LoginException:
/**
* 类描述:登录相
Linux中find常见用法示例
510888780
linux
Linux中find常见用法示例
·find path -option [ -print ] [ -exec -ok command ] {} \;
find命令的参数;
SpringMVC的各种参数绑定方式
Harry642
springMVC 绑定 表单
1. 基本数据类型(以int为例,其他类似):
Controller代码:
@RequestMapping("saysth.do")
public void test(int count) {
}
表单代码:
<form action="saysth.do" method="post&q
Java 获取Oracle ROWID
aijuans
java oracle
A ROWID is an identification tag unique for each row of an Oracle Database table. The ROWID can be thought of as a virtual column, containing the ID for each row.
The oracle.sql.ROWID class i
java获取方法的参数名
antlove
java jdk parameter method reflect
reflect.ClassInformationUtil.java
package reflect;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAtt
JAVA正则表达式匹配 查找 替换 提取操作
百合不是茶
java 正则表达式 替换 提取 查找
正则表达式的查找;主要是用到String类中的split();
String str;
str.split();方法中传入按照什么规则截取,返回一个String数组
常见的截取规则:
str.split("\\.")按照.来截取
str.
Java中equals()与hashCode()方法详解
bijian1013
java set equals() hashCode()
一.equals()方法详解
equals()方法在object类中定义如下:
public boolean equals(Object obj) {
return (this == obj);
}
很明显是对两个对象的地址值进行的比较(即比较引用是否相同)。但是我们知道,String 、Math、I
精通Oracle10编程SQL(4)使用SQL语句
bijian1013
oracle 数据库 plsql
--工资级别表
create table SALGRADE
(
GRADE NUMBER(10),
LOSAL NUMBER(10,2),
HISAL NUMBER(10,2)
)
insert into SALGRADE values(1,0,100);
insert into SALGRADE values(2,100,200);
inser
【Nginx二】Nginx作为静态文件HTTP服务器
bit1129
HTTP服务器
Nginx作为静态文件HTTP服务器
在本地系统中创建/data/www目录,存放html文件(包括index.html)
创建/data/images目录,存放imags图片
在主配置文件中添加http指令
http {
server {
listen 80;
server_name
kafka获得最新partition offset
blackproof
kafka partition offset 最新
kafka获得partition下标,需要用到kafka的simpleconsumer
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.
centos 7安装docker两种方式
ronin47
第一种是采用yum 方式
yum install -y docker
java-60-在O(1)时间删除链表结点
bylijinnan
java
public class DeleteNode_O1_Time {
/**
* Q 60 在O(1)时间删除链表结点
* 给定链表的头指针和一个结点指针(!!),在O(1)时间删除该结点
*
* Assume the list is:
* head->...->nodeToDelete->mNode->nNode->..
nginx利用proxy_cache来缓存文件
cfyme
cache
user zhangy users;
worker_processes 10;
error_log /var/vlogs/nginx_error.log crit;
pid /var/vlogs/nginx.pid;
#Specifies the value for ma
[JWFD开源工作流]JWFD嵌入式语法分析器负号的使用问题
comsci
嵌入式
假如我们需要用JWFD的语法分析模块定义一个带负号的方程式,直接在方程式之前添加负号是不正确的,而必须这样做:
string str01 = "a=3.14;b=2.71;c=0;c-((a*a)+(b*b))"
定义一个0整数c,然后用这个整数c去
如何集成支付宝官方文档
dai_lm
android
官方文档下载地址
https://b.alipay.com/order/productDetail.htm?productId=2012120700377310&tabId=4#ps-tabinfo-hash
集成的必要条件
1. 需要有自己的Server接收支付宝的消息
2. 需要先制作app,然后提交支付宝审核,通过后才能集成
调试的时候估计会真的扣款,请注意
应该在什么时候使用Hadoop
datamachine
hadoop
原帖地址:http://blog.chinaunix.net/uid-301743-id-3925358.html
存档,某些观点与我不谋而合,过度技术化不可取,且hadoop并非万能。
--------------------------------------------万能的分割线--------------------------------
有人问我,“你在大数据和Hado
在GridView中对于有外键的字段使用关联模型进行搜索和排序
dcj3sjt126com
yii
在GridView中使用关联模型进行搜索和排序
首先我们有两个模型它们直接有关联:
class Author extends CActiveRecord {
...
}
class Post extends CActiveRecord {
...
function relations() {
return array(
'
使用NSString 的格式化大全
dcj3sjt126com
Objective-C
格式定义The format specifiers supported by the NSString formatting methods and CFString formatting functions follow the IEEE printf specification; the specifiers are summarized in Table 1. Note that you c
使用activeX插件对象object滚动有重影
蕃薯耀
activeX插件 滚动有重影
使用activeX插件对象object滚动有重影 <object style="width:0;" id="abc" classid="CLSID:D3E3970F-2927-9680-BBB4-5D0889909DF6" codebase="activex/OAX339.CAB#
SpringMVC4零配置
hanqunfeng
springmvc4
基于Servlet3.0规范和SpringMVC4注解式配置方式,实现零xml配置,弄了个小demo,供交流讨论。
项目说明如下:
1.db.sql是项目中用到的表,数据库使用的是oracle11g
2.该项目使用mvn进行管理,私服为自搭建nexus,项目只用到一个第三方 jar,就是oracle的驱动;
3.默认项目为零配置启动,如果需要更改启动方式,请
《开源框架那点事儿16》:缓存相关代码的演变
j2eetop
开源框架
问题引入
上次我参与某个大型项目的优化工作,由于系统要求有比较高的TPS,因此就免不了要使用缓冲。
该项目中用的缓冲比较多,有MemCache,有Redis,有的还需要提供二级缓冲,也就是说应用服务器这层也可以设置一些缓冲。
当然去看相关实现代代码的时候,大致是下面的样子。
[java]
view plain
copy
print
?
public vo
AngularJS浅析
kvhur
JavaScript
概念
AngularJS is a structural framework for dynamic web apps.
了解更多详情请见原文链接:http://www.gbtags.com/gb/share/5726.htm
Directive
扩展html,给html添加声明语句,以便实现自己的需求。对于页面中html元素以ng为前缀的属性名称,ng是angular的命名空间
架构师之jdk的bug排查(一)---------------split的点号陷阱
nannan408
split
1.前言.
jdk1.6的lang包的split方法是有bug的,它不能有效识别A.b.c这种类型,导致截取长度始终是0.而对于其他字符,则无此问题.不知道官方有没有修复这个bug.
2.代码
String[] paths = "object.object2.prop11".split("'");
System.ou
如何对10亿数据量级的mongoDB作高效的全表扫描
quentinXXZ
mongodb
本文链接:
http://quentinXXZ.iteye.com/blog/2149440
一、正常情况下,不应该有这种需求
首先,大家应该有个概念,标题中的这个问题,在大多情况下是一个伪命题,不应该被提出来。要知道,对于一般较大数据量的数据库,全表查询,这种操作一般情况下是不应该出现的,在做正常查询的时候,如果是范围查询,你至少应该要加上limit。
说一下,
C语言算法之水仙花数
qiufeihu
c 算法
/**
* 水仙花数
*/
#include <stdio.h>
#define N 10
int main()
{
int x,y,z;
for(x=1;x<=N;x++)
for(y=0;y<=N;y++)
for(z=0;z<=N;z++)
if(x*100+y*10+z == x*x*x
JSP指令
wyzuomumu
jsp
jsp指令的一般语法格式: <%@ 指令名 属性 =”值 ” %>
常用的三种指令: page,include,taglib
page指令语法形式: <%@ page 属性 1=”值 1” 属性 2=”值 2”%>
include指令语法形式: <%@include file=”relative url”%> (jsp可以通过 include