JUC多并发编程 CompletableFuture

news2024/9/25 1:21:55

Future 接口理论

Future 接口(FutureTask 实现类): 定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等

方法图:

类图:

代码示例: 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyThread2());

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();

        System.out.println(futureTask.get());
    }
}

// 多线程,无返回
class MyThread implements Runnable{

    @Override
    public void run() {

    }
}

// 无返回
class MyThread2 implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("----- come in call()");
        return "hello Callable";
    }
}

优点:

Future + 线程池异步多线程任务配合,能显著提高程序的执行效率

import java.util.concurrent.*;

public class FutureThreadPoolDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        long startTime = System.currentTimeMillis();
        FutureTask<String> futureTask1 = new FutureTask<String>(()-> {
            try{
                TimeUnit.MICROSECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task1 over";
        });
        threadPool.submit(futureTask1);
        FutureTask<String> futureTask2 = new FutureTask<String>(()-> {
            try{
                TimeUnit.MICROSECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task2 over";
        });
        threadPool.submit(futureTask2);

        try{
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(futureTask1.get());
        System.out.println(futureTask2.get());
        System.out.println("---costTime" + (endTime - startTime) + "毫秒");
        System.out.println(Thread.currentThread().getName() + "\t ----end");
        threadPool.shutdown();
    }


    public static void m1(String[] args) {
        long startTime = System.currentTimeMillis();

        try{
            TimeUnit.MICROSECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try{
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try{
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();

        System.out.println("---costTime" + (endTime - startTime) + "毫秒");
        System.out.println(Thread.currentThread().getName() + "\t ----end");

    }
}

缺点:

  • get: 容易导致阻塞,一般建议放在程序后面
  • IsDone 轮询:轮询的方式会耗费无谓的 CPU 资源,而且也不见得能及时地得到计算结果, 如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class FutureApiDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask1 = new FutureTask<String>(()-> {
            try{
                System.out.println(Thread.currentThread().getName() + "\t ---- come in");
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task1 over";
        });

        Thread t1 = new Thread(futureTask1, "t1");
        t1.start();

//        System.out.println(futureTask1.get());  // 非要等到结果才会离开,不管你是否计算完成,  get 容易导致阻塞,一般建议放在程序后面
//        System.out.println(Thread.currentThread().getName() + "\t -- 忙其它任务了");
        while(true) {
            if (futureTask1.isDone()) {
                System.out.println(futureTask1.get());
                break;
            }else {
                try{
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("正在处理中...");
            }
        }
    }
}

CompletableFuture

CompletableFure 提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方

类图:

CompletionStage:

  • CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段 
  • 一个阶段的执行可能被单个阶段的完成触发,也可以由多个阶段一起触发

CompletableFuture:

  • 在 java8 中,CompletableFuture 提供了非常强大的 Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法
  • 它可能代表一个明确完成的 Future,也有可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作
  • 它实现了 Future 和 CompletionStage 接口

四大常用静态方法:

  • 没有执行 Executor 的方法,直接使用默认的 ForkJoinPool.commonPool() 作为它的线程池执行异步代码
  • 如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
类型方法名
runAsync 无返回值public static CompletableFuture<Void> runAsync(Runable runable)
public static CompletableFuture<Void> runAsync(Runable runable, Executor executor)
supplyAsync 有返回值public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

示例代码:

import java.util.concurrent.*;

public class CompletableFutureBuildDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()-> {
//            System.out.println(Thread.currentThread().getName());
//            try{
//                TimeUnit.SECONDS.sleep(1);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }, threadPool);
//        System.out.println(completableFuture.get());

        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()-> {
            System.out.println(Thread.currentThread().getName());
            try{
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello supplyAsync";
        }, threadPool);
        System.out.println(completableFuture.get());

        threadPool.shutdown();
    }
}

减少阻塞和轮询:

可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法

import java.util.concurrent.*;

public class CompletableFutureUseDemo {

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "----come in");
            int result = ThreadLocalRandom.current().nextInt(10);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(result > 5) {
                int i = 10/0;
            }
            System.out.println("---- 1 秒钟后出结果:" + result);
            return result;
        }, threadPool).whenComplete((v, e) -> {
            if(e != null) {
                System.out.println("---- 计算完成,更新系统UpdateValue:" + v);
            }
        }).exceptionally(e-> {
            e.printStackTrace();
            System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());
            return null;
        });
        System.out.println(Thread.currentThread().getName() + " 线程先去忙其他任务");
    }

    public static void future2(String[] args) {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "----come in");
            int result = ThreadLocalRandom.current().nextInt(10);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("---- 1 秒钟后出结果:" + result);
            return result;
        }).whenComplete((v, e) -> {
            if(e != null) {
                System.out.println("---- 计算完成,更新系统UpdateValue:" + v);
            }
        }).exceptionally(e-> {
            e.printStackTrace();
            System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());
            return null;
        });
        System.out.println(Thread.currentThread().getName() + " 线程先去忙其他任务");

        // ForkJoinPool 避免守护线程关闭
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public static void future1() throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "----come in");
            int result = ThreadLocalRandom.current().nextInt(10);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("---- 1 秒钟后出结果:" + result);
            return result;
        });

        System.out.println(Thread.currentThread().getName() + " 线程先去忙其他任务");
        System.out.println(completableFuture.get());
    }
}

CompletableFuture的优点:

  • 异步任务结束时,会自动回调某个对象的方法
  • 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行
  • 异步任务出错时,会自动回调某个对象方法

使用案例:

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class CompletableFutureMallDemo {
    static List<NetMall> list = Arrays.asList(
            new NetMall("jd")
            , new NetMall("dangdang")
            , new NetMall("taobao"));


    public static List<String> getPrice(List<NetMall> list, String productName) {
        return list.stream()
                .map(it-> String.format(productName + "in %s price is %.2f", it.getNetMallName(), it.calcPrice(productName)))
                .collect(Collectors.toList());
    }

    public static List<String> getPriceByCompletableFuture(List<NetMall> list, String productName) {
        return list.stream()
                .map(it-> CompletableFuture.supplyAsync(()-> String.format(productName + "in %s price is %.2f", it.getNetMallName(), it.calcPrice(productName))))
                .collect(Collectors.toList())
                .stream()
                .map(s -> s.join()).collect(Collectors.toList());
    }


    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        List<String> list1 = getPrice(list, "mysql");
        for (String element : list1) {
            System.out.println(element);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("----costTime:" + (endTime - startTime) + "毫秒");

        long startTime2 = System.currentTimeMillis();
        List<String> list2 = getPriceByCompletableFuture(list, "mysql");
        for (String element : list1) {
            System.out.println(element);
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("----costTime:" + (endTime2 - startTime2) + "毫秒");
    }
}


class NetMall{
    private String netMallName;

    public String getNetMallName() {
        return netMallName;
    }

    public NetMall(String netMallName) {
        this.netMallName = netMallName;
    }

    public double calcPrice(String productName) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }
}

常用方法

结果和触发计算:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CompletableFutureAPIDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()-> {
            System.out.println(Thread.currentThread().getName());
            try{
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "hello supplyAsync";
        });
//        System.out.println(completableFuture.get());
//        System.out.println(completableFuture.get(2, TimeUnit.SECONDS));
//        System.out.println(completableFuture.join());
        // 如果正常完成,则返回正常值,否则返回设置的默认值
//        System.out.println(completableFuture.getNow("xxx"));

        // 是否打断 get 方法立即返回括号值
        System.out.println(completableFuture.complete("completeValue") + "\t" + completableFuture.join());
    }
}

计算结果进行处理:

public class CompletableFutureAPI2Demo {

    public static void main(String[] args) {
        // 任务 A 执行完执行 B,并且 B 不需要 A的结果
        System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenRun(()-> {}).join());
        // 任务 A 执行完执行B,B 需要 A的结果,但是任务 B 无返回值
        System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenAccept(r-> System.out.println(r)).join());
        // 任务 A 执行完执行B, B需要 A的结果,同时任务 B 有返回值
        System.out.println(CompletableFuture.supplyAsync(()->"resultA").thenApply(r-> r+ "resultB").join());
    }

    // thenAccept 消费处理,无返回结果
    public static void thenAccept(String[] args) {
        CompletableFuture.supplyAsync(()-> {
            return 1;
        }).thenApply(f-> {
            return f + 2;
        }).thenApply(f-> {
            return f + 3;
        }).thenAccept((v)->{
            System.out.println("消费处理:" + v);
        });
    }

    // handler 可以带着异常继续向下走
    public static void handle() {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        CompletableFuture.supplyAsync(()-> {
            System.out.println(Thread.currentThread().getName());
            int i = 10/0;
            try{
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(1111);
            return 1;
        },threadPool).handle((f,e)-> {
            System.out.println(2222);
            return f + 2;
        }).handle((f,e)-> {
            System.out.println(3333);
            return f + 3;
        }).whenComplete((v, e)->{
            if (e == null) {
                System.out.println("---- 计算结果:" + v);
            }
        }).exceptionally(e-> {
            e.printStackTrace();
            System.out.println(e.getMessage());
            return null;
        });

        System.out.println(Thread.currentThread().getName() + "---- 主线程先去忙其他任务");
    }

    // 如果出现异常,无法走下一步
    public static void thenApply() {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        CompletableFuture.supplyAsync(()-> {
            System.out.println(Thread.currentThread().getName());
            try{
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(1111);
            return 1;
        },threadPool).thenApply(f-> {
            System.out.println(2222);
            return f + 2;
        }).thenApply(f-> {
            System.out.println(3333);
            return f + 3;
        }).whenComplete((v, e)->{
            if (e == null) {
                System.out.println("---- 计算结果:" + v);
            }
        }).exceptionally(e-> {
            e.printStackTrace();
            System.out.println(e.getMessage());
            return null;
        });

        System.out.println(Thread.currentThread().getName() + "---- 主线程先去忙其他任务");
    }
}

CompletableFuture 和 线程池:

  • 没有传入自定义线程池,都是默认线程池 ForkJoinPool
  • 传入了一个自定义线程池,如果第一个执行第一个任务的时候,传入了一个自定义线程池,调用 thenRun 方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。调用 thenRunAsync 执行第二个任务时,则第一个任务使用的你自己传入的线程池,第二个任务使用的是 ForkJoin 线程池
  • 有可能处理太快,系统优化切换原则,直接使用 main 线程处理
import java.util.concurrent.*;

public class CompletableFutureWithThreadPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("1 号任务\t" + Thread.currentThread().getName());
            try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
            return "hello";
        }, threadPool).thenRun(() -> {
            try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("2 号任务\t" + Thread.currentThread().getName());
        }).thenRunAsync(() -> {
            try{ TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("3 号任务\t" + Thread.currentThread().getName());
        }).thenRun(() -> {
            try{ TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("4 号任务\t" + Thread.currentThread().getName());
        });

        System.out.println(completableFuture.get(2, TimeUnit.SECONDS));
    }
}

计算速度选用:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureFastDemo {
    public static void main(String[] args) {
        CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
            System.out.println("A come in");
            try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
            return "playA";
        });

        CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in");
            try{ TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            return "playB";
        });

        CompletableFuture<String> result = playA.applyToEither(playB, f -> {
            return f + " is winer";
        });

        System.out.println(Thread.currentThread().getName() + "\t----" + result.join());
    }
}

计算结果进行合并:

  • 两个 CompletionStage 任务都完成后,最终能把两个任务的结果一起交给 thenCombine 来处理
  • 先完成的先等着,等待其他分支任务
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureCombineDemo {
    public static void main(String[] args) {
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\n" + "启动了");
            try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            return 10;
        });

        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\n" + "启动了");
            try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
            return 20;
        });

        CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
            System.out.println("------ 开始两个结果合并");
            return x + y;
        });

        System.out.println(Thread.currentThread().getName() + "\t----" + result.join());
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430713.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Maven聚合开发【实例详解---5555字】

目录 一、Maven聚合开发_继承关系 二、Maven聚合案例 1. 搭建dao模块 2. 搭建service模块 3. 搭建web模块 4. 运行项目 一、Maven聚合开发_继承关系 Maven中的继承是针对于父工程和子工程。父工程定义的依赖和插件子工程可以直接使用。注意父工程类型一定为POM类型工程…

数字电路学习笔记 门电路概述

1 高低电平的实现 在数字电路中&#xff0c;输入输出都是二值逻辑&#xff0c;其高低电平用“0”和“1”表示其高低电平的获得是通过开关电路来实现&#xff0c;如二极管或三极管电路组成。如图 高低电平实现原理电其原理电路 当开关 S 断开时&#xff0c;输出电压 v。 V。&am…

Alibaba开源的Java诊断工具Arthas-实战

目录参考一、启动二、支持的ognl表达式三、监听参数监听Controller 的参数和返回值监听完整参数和返回值监听kafka消费监听单个参数监听异常按照耗时进行过滤监听参数比较四、变量和方法查询静态成员变量值查询配置类具体属性的值通过类加载器查看Spring容器中对象所有属性执行…

Qt显示数学公式

文章目录一、前言二、效果展示三、库文件四、使用教程五、MathML语法5.1、顶层元素5.2、字符/符号元素5.3、通用布局元素5.4、边标和角标元素5.5、表格教学5.6、数学符号六、转换工具6.1、手写转换公式工具myscript6.2、截图转换公式工具Mathpix一、前言 目前项目中需要显示数…

MySQL数据库学习——约束——概述+演示

我们先创建一个表&#xff1a; create database itheima; use itheima; create table user(id int primary key auto_increment comment 主键, name varchar(10) not null unique comment 姓名,age int check ( age > 0 && age <120 ) comment 年龄,status char…

零代码是什么?零代码平台适合谁用?

随着信息技术的发展&#xff0c;软件开发领域也不断发生变革&#xff0c;零代码&#xff08;No-Code&#xff09;开发模式越来越受到关注。 零代码到底是什么&#xff0c;能不能用通俗的话来说&#xff1f;这就来给大家讲一讲&#xff01; 01 零代码为什么出现&#xff1f; 随…

spring cloud consul服务注册源码分析

我们注册在consul上的服务&#xff0c;都是通过spring cloud consul discorvery来实现的&#xff0c;可以通过maven依赖导入spring-cloud-consul-discovery包。 对于spring项目&#xff0c;首先查看spring.factories文件&#xff1a; 从源码中可以找到服务注册、自动服务注册…

kubespray v2.21.0 部署 kubernetes v1.24.0 集群

文章目录1. 前言2. 创建7台虚拟机3. 部署 git3.1 dnf 安装3.2 tar 安装4. 下载 kubespray 介质5. 配置 zsh 终端6. 配置互信7. 安装 docker-ce8. 安装 ansible9. 安装其他依赖10. 配置内核参数11. 安装 k8s利用官方默认镜像部署 k8s 集群利用自定义构建镜像部署 k8s 集群12. 配…

第二讲 第一个Python程序

在上一课中&#xff0c;我们对 Python 语言的过去现在有了一些了解&#xff0c;我们准备好了运行 Python 程序所需要的解释器环境。相信大家已经迫不及待的想开始自己的 Python 编程之旅了&#xff0c;但是新问题来了&#xff0c;我们应该在什么地方书写 Python 程序&#xff0…

mvn测试执行用例的常用的命令

mvn执行指定的测试用例 mvn常用的命令 mvn mvn test -Dtestxxx&#xff1a;执行指定测试用例的命令&#xff0c;后面的xxx就是指定的所有被标记的用例 mvn test -Denv环境名 -Dtest包的路径.*:指定具体的某个环境的某个包下的所用例 mvn test -Dgroupsxxx -Dtestxxx :指定某…

Python实战案例:采集P站数据内容

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 开发环境&#xff1a; Python 3.8 / 编译器 Pycharm 2021.2版本 / 编辑器 模块使用&#xff1a; requests &#xff1a; 主要用来发 送 HTTP 请求 、 属于第三方模块 parsel &#xff1a; 解析html&#xff0c;xml内…

业务高峰期,人力需求突增,灵活用工成破局关键

在上一篇文章里&#xff0c;讲到了盖雅零工平台如何帮助HR管理复杂、灵活的零工人员。 而零工平台是否好用&#xff0c;不仅要便于HR管理零工&#xff0c;更需要终端的业务管理者觉得方便、实用、操作顺畅。 由于零工主要被用来解决突增的、巅峰期的、季节性的用工需求&#…

nginx实战,nginx高可用, nginx负载配置, nginx正向,反向代理,nginx各种配置, 及其配置问题

nginx配置实战, nginx负载&#xff0c; nginx正向&#xff0c;反向代理&#xff0c;nginx路由配置 nginxnginx基础nginx 配置nginx正向代理nginx 反向代理nginx 负载nginx高可用 nginx 配置常见问题反向代理报426错误post请求变get请求nginx 配置前端代理&#xff0c; 会出现js…

自学Java靠谱吗?自学Java能找到工作吗?建议收藏反复查看!

网上是不是很多人劝你学Java不要报班&#xff0c;浪费金钱&#xff0c;自己在家学习Java就可以了。那小源问你&#xff0c;自学Java它的成功率到底有多少&#xff1f;前两天看到一位老师发的视频&#xff0c;说自学的成功率大概在5%左右&#xff0c;小源觉得这个数据还是比较客…

IO线程模型

文章目录IO线程模型一、BIO1、概念2、Demo2.1、Demo1.02.2、Demo2.02.3、小结二、NIO1、概念2、Demo2.1、Demo1.02.2、Demo2.0IO线程模型 一、BIO 1、概念 BIO 全称 Block-IO 是一种**同步且阻塞**的通信模式。是一个比较传统的通信方式&#xff0c;模式简单&#xff0c;使用…

万字长文的BI百科全解

目前来看&#xff0c;现今世界未来的发展方向基本已经确定&#xff0c;数字化的趋势已经化身为一股不可阻挡的浪潮&#xff0c;各国也都宣布了数字化、数据、数字经济、数字化转型等方面的相关发展政策法规&#xff0c;明确未来的战略方针。同时世界传统经济增长也开始乏力&…

6 计时器(一)

计时器 6.1 TIM TIM简介 TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中…

2023 年 3 月 NFT 月度报告

作者&#xff1a;Danielfootprint.network 数据来源&#xff1a;NFT Monthly Report 三月份的 NFT 市场上出现了两个有趣的趋势。一方面&#xff0c;Polygon 链尽管在二月份有所突破&#xff0c;达到了 NFT 总交易量的 4.2%&#xff0c;但于三月再次跌至 1% 以下&#xff0c;…

55 openEuler搭建Mariadb数据库服务器-配置环境

文章目录 55 openEuler搭建Mariadb数据库服务器-配置环境55.1 关闭防火墙并取消开机自启动55.2 修改SELINUX为disabled55.3 创建组和用户55.4 创建数据盘55.4.1 方法一&#xff1a;在root权限下使用fdisk进行磁盘管理55.4.2 方法二&#xff1a;在root权限下使用LVM进行磁盘管理…

【C语言】基础语法1:变量和数据类型

❤️‍&#x1f525;前情提要❤️‍&#x1f525;   欢迎来到C语言基本语法教程   在本专栏结束后会将所有内容整理成思维导图&#xff08;结束换链接&#xff09;并免费提供给大家学习&#xff0c;希望大家纠错指正。本专栏将以基础出发&#xff0c;在之后的教程中将会不断…