高并发编程(The Executive Framework)

本文介绍了Java中线程池的原理与应用,包括使用Executors创建线程池、Callable与Future接口的异步任务处理,以及CompletableFuture、Asynchronous API、异步链、异常处理和结果转换等高级特性。通过实例演示了如何优化高并发场景和提升程序响应速度。
摘要由CSDN通过智能技术生成


活动地址:CSDN21天学习挑战赛

7.1 线程池

高并发编程要解决的核心问题:由于创建单独的线程是需要消耗一定的系统资源的,因此线程的数量在一定时间是有限的。

使用一个单独的线程执行任务:

  • 需要向系统申请一定的空间资源

  • 同时,这个过程(创建和销毁)也需要耗费一定时间

从JDK 5开始,提供了线程池(Thread Pool)来解决这个问题。线程池,就是存放多个线程的一个容器,这里面的线程可以反复使用。线程池的工作模式具有以下特点:

  • 当一个线程执行完任务后,会重新放回到线程池(而不是销毁),这样它可以继续在执行后续的任务中得到重用。

  • 线程池中的线程是相对固定和有限的,因此不会因为依据太多的任务创建太多的线程而导致Out Of Memory,OOM。

  • 同时,线程池还会管理如何将任务分配给执行的线程来执行:当所有线程都处于繁忙时,那么任务将会被放在一个队列(Queue)中等待,只要有一个线程空闲了,就会从队列中取出一个任务分配给这个空闲的线程来执行。

有了线程池,我们就不需要直接来创建线程,而只需要向线程池提交任务,有线程池来进行管理任务的具体分配。

7.2 Executors

public class Main {
    public static void main(String[] args) {
        var service = Executors.newFixedThreadPool(2);
        try {
            for (int i = 0; i < 5; i++) {
                service.submit(() -> System.out.println(Thread.currentThread().getName()));
            }
        } finally {
            service.shutdown();
        }
    }
}

7.3 Callable & Future

我们还可以使用Callable接口来提交有返回值的任务,得到Feature作为结果

public class LongTask {
    public static void simulate(int ms){
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
​
public class Main {
    public static void main(String[] args) {
        var service = Executors.newFixedThreadPool(2);
        try {
            var future = service.submit(() -> {
                LongTask.simulate(3_000);
                return 1;
            });
            System.out.println("...");
            var result = future.get();
            System.out.println(result);
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            service.shutdown();
        }
    }
}

在提交任务之后,程序会立即返回Future对象作为返回值,但是任务的返回结果并没有得到,一直等到任务执行完毕,我们才能获取到任务执行的结果。(submit方法并不会阻塞程序,但是get方法会阻塞直至任务执行完毕得到返回值)。

7.4 异步编程

在上述案例中,由于最终获取到任务执行结果是需要消耗时间的,因此在此期间,get方法会一直处于阻塞状态。在主线程上,阻塞状态是非常不理想的,因为,它会降低整个程序的响应速度,比如主线程还有其它的事情要做:接收用户的点击事件,同步更新页面上其它数据,等等。这种场景,程序就处于同步状态(synchronous)。为了提高整个应用程序的响应速度和用户体验,我们需要继续对上述代码进行异步化处理(asynchronous)。

7.4.1 CompletableFuture

public class Main {
    public static void main(String[] args) {
//        Runnable task = () -> System.out.println("a");
//        var future = CompletableFuture.runAsync(task);
​
        Supplier<Integer> supplier = () -> 1;
        var future = CompletableFuture.supplyAsync(supplier);
        try {
            var result = future.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

7.4.2 Asynchronous API

常规异步案例:

public class MailService {
    public void send() {
        System.out.println("发送邮件ing...");
        LongTask.simulate(3_000);
        System.out.println("发送完毕...");
    }
​
    public CompletableFuture<Void> sendAsync() {
        return CompletableFuture.runAsync(this::send);
    }
}
​
public class Main {
    public static void main(String[] args) {
        var service = new MailService();
        service.sendAsync();
        System.out.println("...");
        LongTask.simulate(5_000);
    }
}

7.4.3 异步链

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步逻辑");
            return 1;
        });
        future.thenRunAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("后续逻辑.");
        });
    }
}

异步任务有返回值,在后续任务中获取异步任务处理结果:

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步逻辑");
            return 1;
        });
        future.thenRunAsync(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("后续逻辑.");
        });
    }
}

7.4.4 异常处理

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步逻辑");
            throw new IllegalStateException();
        });
    }
}

在上述案例中,如果异步任务中如果发生异常的话,那么在主线程中是获取不到的(因为异常是在另一个新的线程中发生的),如果要获取的异常信息并进行相应处理的话,需要如下编码:

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步逻辑");
            throw new IllegalStateException();
        });
​
        try {
//            var result = future.get();
            var result = future.exceptionally(ex -> "默认值").get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

如果直接在future上调用get(),我们将会得到异常;如果在此之前,调用exceptionally()将会得到一个新的CompletableFuture对象,我们可以在其中返回一个当异常发生时的默认值作为处理手段。

7.4.5 转换返回结果

对上一次的异步任务的返回结果进行数据类型的转换:

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> 26);
        future.thenApply(c -> Float.toString(c * 1.8f + 32))
                .thenAccept(System.out::println);
​
    }
}

7.4.6 Compose组合

public class Main {
    public static CompletableFuture<String> findEmailByIdAsync(int id) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("根据id:" + id + "查询DB");
            return "a@b.com";
        });
    }
​
    public static CompletableFuture<String> findListByEmailAsync(String email) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("根据email:" + email + "查询列表");
            return "播放列表";
        });
    }
​
    public static void main(String[] args) {
        findEmailByIdAsync(1)
                .thenCompose(Main::findListByEmailAsync)
                .thenAccept(System.out::println);
        LongTask.simulate(1000);
    }
}

7.4.7 Combine结果组合

public class Main {
    public static void main(String[] args) {
        var task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("获取结算金额(美元)");
            return 20;
        });
        var task2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("获取结算汇率");
            return 0.9;
        });
        task1.thenCombine(task2, (price, rate) -> price * rate)
                .thenAcceptAsync(System.out::println);
    }
}

7.4.8 多任务等待

等待所有任务:

public class Main {
    public static void main(String[] args) {
        var task1 = CompletableFuture.supplyAsync(() -> 1);
        var task2 = CompletableFuture.supplyAsync(() -> 2);
        var task3 = CompletableFuture.supplyAsync(() -> 3);
        var all = CompletableFuture.allOf(task1, task2, task3);
        all.thenRun(() -> {
            System.out.println("后续逻辑");
            try {
                var res1 = task1.get();
                System.out.println(res1);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
    }
}

等待首个完成的任务(竞争问题):

public class Main {
    public static void main(String[] args) {
        var task1 = CompletableFuture.supplyAsync(() -> {
            LongTask.simulate(1000);
            return 1;
        });
        var task2 = CompletableFuture.supplyAsync(() -> 2);
        CompletableFuture.anyOf(task1, task2)
                .thenAccept(System.out::println);
    }
}

7.4.9 超时任务

public class Main {
    public static void main(String[] args) {
        var future = CompletableFuture.supplyAsync(() -> {
            LongTask.simulate(3000);
            return 1;
        });
        try {
            var result = future.completeOnTimeout(0, 1, TimeUnit.SECONDS)
                    .get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

7.5 Project 询价

向各个航空公司售票代理询价,一旦有询价结果,便输出显示

public class Quote {
    private String name;
    private int price;

    public Quote(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Quote{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

public class FlightService {
    private final Random random = new Random();

    public Stream<CompletableFuture<Quote>> query() {
        var list = List.of("a", "b", "c");
        return list.stream()
                .map(this::query);
    }

    public CompletableFuture<Quote> query(String name) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println("询价:" + name);
            var delay = random.nextInt(2_000) + 1_000;
            LongTask.simulate(delay);
            var price = random.nextInt(10) + 100;
            return new Quote(name, price);
        });
    }
}

public class Main {
    public static void main(String[] args) {
        var start = LocalTime.now();
        var service = new FlightService();
        var arr = service
                .query()
                .map(future -> future.thenAccept(System.out::println))
                .toArray(CompletableFuture[]::new);

        var all = CompletableFuture.allOf(arr);
        all.thenRun(() -> {
            var end = LocalTime.now();
            var duration = Duration.between(start, end);
            System.out.println("总耗时:" + duration.toMillis());
        });
        LongTask.simulate(3_000);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值