深度解析java异步多线程优化版

news2024/11/24 15:01:52

快速使用需求:我不要理解一堆理论想直接用 操作说明

ITask.java
PutEsTask.java
TaskExecutor.java
TaskQueue.java
TestMain.java

请把这几个类文件复制下去,运行testMain的方法,根据TestMain的运行日志,【1】-> 【8】不同需求的逻辑需求,组装你的一步多线程队列即可。

精解需求:场景带入


快速理解小场景描述:
【一群人】来到【一个大厅】办理业务,大厅中有【多个窗口】给我们办理业务。
每个人都有自己要办事情,处理过程需要消耗时间。
大厅根据人群多少,开始窗口梳理。

如果把“一群人”理解成一群待处理的n个【任务】,把这群人排成一个长队就形成了一个【任务队列】,“多个窗口”充当我们的【多个线程】异步处理任务队列。我们多线程解决任务队列的代入感来了,有木有!
“大厅”用来充当线程和任务的组装以及处理关系。如:大厅营业start:所有窗口等待办公创建多线程,大厅stop:所有窗口关闭,回收线程。
接下来,就是多个线程异步处理队列任务的干货!

任务类接口


1.首先使我们的任务接口
把这个任务设计成接口,为了后续我们使用的方便宜行,后续去实现接口,我们可以是发短信任务,发邮件任务,等待下载任务等等。
文件:ITask.java

package com.sboot.blog.task;

/**
 * 任务的执行体或者携带体 理解成去窗口办事的人
 *
 * @author zhaoxinglu
 */
public interface ITask {
    /**
     * 执行体中 自定义任务内容
     */
    void run();
}

线程类

2.我们的线程类
这里也就是大厅窗口的一个建设,用来处理任务
文件:TaskExecutor.java
主要任务是:管理每个线程对任务的take()的持有

package com.atguigu.gulimall.search.morethread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.BlockingQueue;

/**
 * 处理任务的窗口 窗口上班 就位执行体 处理费时的任务
 *
 *
 */
public class TaskExecutor extends Thread {
    /**
     * 执行体队列
     */
    private BlockingQueue<ITask> taskQueue;

    /**
     * 窗口当前事务处理状态 初始化默认开启
     */
    private boolean isRunning = true;

    /**
     * 窗口名字 方便观察线程
     */
    private String taskName;

    public TaskExecutor(BlockingQueue<ITask> taskQueue){
        this.taskQueue = taskQueue;
        this.taskName = makeName();
    }

    /**
     * 生产窗口名字
     */
    public String makeName(){
        String str= "abcdefghijklmnopqrstuvwxyzABCDEFGHIKJLMNOPQRSTUVWXYZ";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<6;++i){
            int number = random.nextInt(52);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 窗口工作状态关闭
     */
    public void quit(){
        isRunning = false;
        interrupt();
    }

    @Override
    public void run(){
        while (isRunning){
            ITask iTask;

            try{
                iTask = taskQueue.take();

                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
                System.out.println("【4.0】TaskExecutor类的窗口报:窗口["+taskName+"_"+Thread.currentThread().getName() +"]窗口领取了一个任务,后面还有任务数:"
                                + taskQueue.size()
                        +df.format(new Date())
                         );
            }catch (InterruptedException e){
                if(!isRunning){
                    interrupt();
                    break;
                }
                continue;
            }
            iTask.run();
        }

    }

}

任务列表和多线程

3.任务队列和多线程的调配
文件:TaskQueue.java
主要功能:初始化窗口数量(线程数)、增加任务队列、查看任务队列长度、窗口(线程)任务开启、窗口(线程)任务关闭

package com.sboot.blog.task;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 任务队列
 * 控制执行体和处理窗口的任务队列
 *
 * @author zhaoxinglu
 */
public class TaskQueue {
    /**
     * 某场景下 排队办事的执行体
     */
    private BlockingQueue<ITask> mTaskQueue;

    /**
     * 某场景下 处理执行体的多个窗口
     */
    public TaskExecutor[] mTaskExecutors;

    /**
     * 创建队列的时候 设定窗口数量
     *
     * @param size
     */
    public TaskQueue(int size) {
        mTaskQueue = new LinkedBlockingQueue<>();
        mTaskExecutors = new TaskExecutor[size];
    }

    /**
     * 场景开始启动
     */
    public void start() {
        //防止存在未关闭窗口  如果有先关闭
        stop();
        //所有窗口状态:等待处理事务
        for (int i = 0; i < mTaskExecutors.length; i++) {
            //每初始化一个窗口 都让窗口观望当前执行体队列mTaskQueue
            mTaskExecutors[i] = new TaskExecutor(mTaskQueue);
            mTaskExecutors[i].start();
        }

    }

    /**
     * 场景关闭 所有窗口关闭
     */
    public void stop() {
        if (mTaskExecutors != null) {
            for (TaskExecutor taskExecutor : mTaskExecutors) {
                if (taskExecutor != null) {
                    taskExecutor.quit();
                }
            }
        }
    }

    /**
     * 允许执行体添加进来
     *
     * @param task
     * @param <T>
     * @return
     */
    public <T extends ITask> int add(T task) {
        if (!mTaskQueue.contains((task))) {
            mTaskQueue.add(task);
        }
        //返回当前排队的执行体数
        return mTaskQueue.size();
    }

    public int getTaskQueueSize(){
        return mTaskQueue.size();
    }

}

实验

4.实例化一种任务 这里模拟打印机功能
文件:PutEsTask.java
这是对任务接口的一个实例化,不建议这么引用,建议采纳闭包写法动态视力话任务,并且内置countDownLatch.countDown(); 逻辑。这个在后头讲,这里我们先做个样例测试。

package com.atguigu.gulimall.search.morethread;

import java.text.SimpleDateFormat;
import java.util.Date;

public class PutEsTask implements ITask{

    @Override
    public void run(){
        try{
            /**
             * 放置任务有重写 这段逻辑被重写 就不再执行
             */

            Thread.sleep(2000);
           // System.out.println("执行体:"+ Thread.currentThread().getName());

            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            System.out.println("任务报:当前【"+Thread.currentThread().getName()+"】带我执行任务PutES working!"+ df.format(new Date()));
        }catch (InterruptedException ignored){

        }

    }


}

样例测试


4.1.控制器中根据业务逻辑生成任务队列,创建多线程实现异步任务队列

package com.atguigu.gulimall.search.morethread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class TestMain {

    public static void main(String[] args) throws Exception {
        /**
         * 单纯的异步多线程
         */
        testTask();
    }
    /**
     * 单纯的异步多线程
     */
    public static void testTask(){
        //设置线程数量
        TaskQueue taskQueue = new TaskQueue(4);
        taskQueue.start();
        System.out.println("工作窗口数:"+taskQueue.mTaskExecutors.length);
        for(int i = 1 ;i<=10;i++) {
            PutEsTask task = new PutEsTask();
            taskQueue.add(task);
        }
    }
}

测试示例结果:

Connected to the target VM, address: '127.0.0.1:60067', transport: 'socket'
工作窗口数:4
【4.0】TaskExecutor类的窗口报:窗口[ynYFjh_Thread-2]窗口领取了一个任务,后面还有任务数:62023-06-06 11:17:31:426
【4.0】TaskExecutor类的窗口报:窗口[aHVPVK_Thread-0]窗口领取了一个任务,后面还有任务数:62023-06-06 11:17:31:426
【4.0】TaskExecutor类的窗口报:窗口[iHlchE_Thread-1]窗口领取了一个任务,后面还有任务数:62023-06-06 11:17:31:426
【4.0】TaskExecutor类的窗口报:窗口[JOnnih_Thread-3]窗口领取了一个任务,后面还有任务数:62023-06-06 11:17:31:426
任务报:当前【Thread-2】带我执行任务PutES working!2023-06-06 11:17:33
【4.0】TaskExecutor类的窗口报:窗口[ynYFjh_Thread-2]窗口领取了一个任务,后面还有任务数:52023-06-06 11:17:33:438
任务报:当前【Thread-3】带我执行任务PutES working!2023-06-06 11:17:33
任务报:当前【Thread-0】带我执行任务PutES working!2023-06-06 11:17:33
【4.0】TaskExecutor类的窗口报:窗口[JOnnih_Thread-3]窗口领取了一个任务,后面还有任务数:32023-06-06 11:17:33:439
任务报:当前【Thread-1】带我执行任务PutES working!2023-06-06 11:17:33
【4.0】TaskExecutor类的窗口报:窗口[aHVPVK_Thread-0]窗口领取了一个任务,后面还有任务数:32023-06-06 11:17:33:439
【4.0】TaskExecutor类的窗口报:窗口[iHlchE_Thread-1]窗口领取了一个任务,后面还有任务数:22023-06-06 11:17:33:441
任务报:当前【Thread-0】带我执行任务PutES working!2023-06-06 11:17:35
任务报:当前【Thread-3】带我执行任务PutES working!2023-06-06 11:17:35
任务报:当前【Thread-2】带我执行任务PutES working!2023-06-06 11:17:35
任务报:当前【Thread-1】带我执行任务PutES working!2023-06-06 11:17:35
【4.0】TaskExecutor类的窗口报:窗口[aHVPVK_Thread-0]窗口领取了一个任务,后面还有任务数:02023-06-06 11:17:35:450
【4.0】TaskExecutor类的窗口报:窗口[JOnnih_Thread-3]窗口领取了一个任务,后面还有任务数:02023-06-06 11:17:35:450
任务报:当前【Thread-3】带我执行任务PutES working!2023-06-06 11:17:37
任务报:当前【Thread-0】带我执行任务PutES working!2023-06-06 11:17:37

闭包动态控制各个环节任务


文件 TestMain.java
5.控制器中根据业务逻辑生成任务队列,创建多线程实现异步任务队列

package com.atguigu.gulimall.search.morethread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;

public class TestMain {

    public static void main(String[] args) throws Exception {
        /**
         * 单纯的异步多线程
         */
//        testTask();
        /**
         * 异步多线程
         * 附有队列结束回收资源或者其他逻辑
         * 设置线程数量
         */
        TaskQueue taskQueue = new TaskQueue(4);
        taskQueue.start();
        System.out.println("工作窗口数(异步线程数):"+taskQueue.mTaskExecutors.length);

        //等待执行完毕后回收多线程资源
        CountDownLatch countDownLatch = new CountDownLatch(10);
        countDownLatch.getCount();
        for(int i = 1 ;i<=10;i++){
        PutEsTask task = new PutEsTask();
//            PutEsTask task = new PutEsTask();
            int n = taskQueue.add(new PutEsTask() {
                @Override
                public void run() {
                    try{
                        Thread.sleep(2000);
                        System.out.println("【4.1】执行体:"+ Thread.currentThread().getName()
                        + " countDownLatch:"+ countDownLatch.getCount()
                        );
                        countDownLatch.countDown();
                    }catch (InterruptedException ignored){
                        System.out.println(ignored.getMessage());
                    }
                }
            });
            System.out.println("【1】放置每个执行体的操作"+i+" 个时  队列内:" +n);
        }

        /**
         * 这个try的核心【countDownLatch.await()】
         * 异步多线程投放任务队列瞬间完成 countDownLatch.getCount() 为队列长度
         * 这个try就一直等待队列逻辑 countDownLatch.countDown() --至0
         * 在执行【countDownLatch.await()】  后面的逻辑
         *
         */
        System.out.println("【2】【完成队列投放后,马上执行逻辑】");
        try {
            System.out.println("【3】阻塞前逻辑 :"+ " countDownLatch:"+ countDownLatch.getCount());
            countDownLatch.await();
            System.out.println("【5】阻塞后:回收进程区域:"+ " countDownLatch:"+ countDownLatch.getCount());
            /**
             * 代码体 【多线程回收前】
             * 在异步队列结束后的逻辑操作代码区
             */
            /*------*/
            System.out.println("【6】【队列结束后,多线程回收前 多线程存活状态】");
            taskQueue.stop();
            /**
             * 代码体 【多线程回收后】
             * 在异步队列结束后的逻辑操作代码区
             */
            /*------*/
            System.out.println("【7】【队列结束后,多线程回收前 多线程回收完毕】");
            //System.out.println("回收进程区域完毕 确认 stop 窗口数:"+taskQueue.mTaskExecutors.length);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        /**
         * 投放队列后完马上执行
         */
        System.out.println("【8】整个队列完整执行后 ");


    }


    /**
     * 单纯的异步多线程
     */
    public static void testTask(){
        TaskQueue taskQueue = new TaskQueue(4);
        taskQueue.start();
        System.out.println("工作窗口数:"+taskQueue.mTaskExecutors.length);
        for(int i = 1 ;i<=10;i++) {
            PutEsTask task = new PutEsTask();
            taskQueue.add(task);
        }
    }
}

闭包动态全控示例测试

Connected to the target VM, address: '127.0.0.1:60299', transport: 'socket'
工作窗口数(异步线程数):4
【1】放置每个执行体的操作1 个时  队列内:1
【1】放置每个执行体的操作2 个时  队列内:1
【1】放置每个执行体的操作3 个时  队列内:1
【1】放置每个执行体的操作4 个时  队列内:2
【1】放置每个执行体的操作5 个时  队列内:3
【1】放置每个执行体的操作6 个时  队列内:4
【1】放置每个执行体的操作7 个时  队列内:5
【1】放置每个执行体的操作8 个时  队列内:5
【1】放置每个执行体的操作9 个时  队列内:6
【1】放置每个执行体的操作10 个时  队列内:7
【2】【完成队列投放后,马上执行逻辑】
【3】阻塞前逻辑 : countDownLatch:10
【4.0】TaskExecutor类的窗口报:窗口[tPwCZX_Thread-3]窗口领取了一个任务,后面还有任务数:62023-06-06 11:18:54:865
【4.0】TaskExecutor类的窗口报:窗口[ToyleP_Thread-2]窗口领取了一个任务,后面还有任务数:62023-06-06 11:18:54:865
【4.0】TaskExecutor类的窗口报:窗口[TQYJuk_Thread-0]窗口领取了一个任务,后面还有任务数:62023-06-06 11:18:54:865
【4.0】TaskExecutor类的窗口报:窗口[aBlIlW_Thread-1]窗口领取了一个任务,后面还有任务数:62023-06-06 11:18:54:865
【4.1】执行体:Thread-1 countDownLatch:10
【4.1】执行体:Thread-3 countDownLatch:10
【4.1】执行体:Thread-0 countDownLatch:10
【4.1】执行体:Thread-2 countDownLatch:10
【4.0】TaskExecutor类的窗口报:窗口[ToyleP_Thread-2]窗口领取了一个任务,后面还有任务数:22023-06-06 11:18:56:873
【4.0】TaskExecutor类的窗口报:窗口[aBlIlW_Thread-1]窗口领取了一个任务,后面还有任务数:22023-06-06 11:18:56:873
【4.0】TaskExecutor类的窗口报:窗口[TQYJuk_Thread-0]窗口领取了一个任务,后面还有任务数:22023-06-06 11:18:56:874
【4.0】TaskExecutor类的窗口报:窗口[tPwCZX_Thread-3]窗口领取了一个任务,后面还有任务数:22023-06-06 11:18:56:873
【4.1】执行体:Thread-1 countDownLatch:6
【4.1】执行体:Thread-2 countDownLatch:6
【4.1】执行体:Thread-0 countDownLatch:6
【4.1】执行体:Thread-3 countDownLatch:5
【4.0】TaskExecutor类的窗口报:窗口[ToyleP_Thread-2]窗口领取了一个任务,后面还有任务数:02023-06-06 11:18:58:879
【4.0】TaskExecutor类的窗口报:窗口[aBlIlW_Thread-1]窗口领取了一个任务,后面还有任务数:02023-06-06 11:18:58:879
【4.1】执行体:Thread-1 countDownLatch:2
【4.1】执行体:Thread-2 countDownLatch:2
【5】阻塞后:回收进程区域: countDownLatch:0
【6】【队列结束后,多线程回收前 多线程存活状态】
【7】【队列结束后,多线程回收前 多线程回收完毕】
【8】整个队列完整执行后 
Disconnected from the target VM, address: '127.0.0.1:60299', transport: 'socket'

工具观察

借助工具:
在这里插入图片描述
运用工具 监控线程:
建立8个线程:
我们不主动回收 完全靠JC回收的状态:
在这里插入图片描述

在这里插入图片描述
我们在任务执行完毕后回收:
在这里插入图片描述
在这里插入图片描述


间隔20s执行带有回收机制图形:
在这里插入图片描述


间隔20s无回收调用图形:
在这里插入图片描述

小结

小总结:
如果这个异步线程调用比较频繁,如我们最后这两张图的展示间隔频率20s,此时在JC机制和JVM还没来得及自行回收线程,我们的下一次调用线程已经出发,会造成我们的线程出线性增长,这样就可能是一个比较危险的操作。条件允许,自行要把进程收回!小波浪稳定性线形图,肯定要比折线增长的安全!

具体主动回收,还是等待机制自己处理,这个需要看我们实际应用的业务场景!
至此结束!


  • 多线程
    多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” 。
    在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些掌握机器低级语言的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。中断对那些实时性很强的任务来说是很有必要的。但对于其他许多问题,只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求 。
    最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。根据前面的论述,大家可能感觉线程处理非常简单。但必须注意一个问题:共享资源!如果有多个线程同时运行,而且它们试图访问相同的资源,就会遇到一个问题。举个例子来说,两个线程不能将信息同时发送给一台打印机。为解决这个问题,对那些可共享的资源来说(比如打印机),它们在使用期间必须进入锁定状态。所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。

线程是进程中的一部分,也是进程的的实际运作单位,它也是操作系统中的最小运算调度单位。进程中的一个单一顺序的控制流就是一条线程,多个线程可以在一个进程中并发。可以使用多线程技术来提高运行效率。
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的 。

  • 异步
    一、异步和多线程有什么区别?其实,异步是目的,而多线程是实现这个目的的方法。异步是说,A发起一个操作后(一般都是比较耗时的操作,如果不耗时的操作就没有必要异步了),可以继续自顾自的处理它自己的事儿,不用干等着这个耗时操作返回。.Net中的这种异步编程模型,就简化了多线程编程,我们甚至都不用去关心Thread类,就可以做一个异步操作出来。

二、随着拥有多个硬线程CPU(超线程、双核)的普及,多线程和异步操作等并发程序设计方法也受到了更多的关注和讨论。本文主要是想探讨一下如何使用并发来最大化程序的性能

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

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

相关文章

2.3 网络设计与redis、memcached、nginx组件

目录 一、网络模块需要处理哪些事情二、reactor网络设计模型三、网络模块与业务的关系四、redis、memcached、nginx1、redis2、memcached3、ngnix4、总结 一、网络模块需要处理哪些事情 网络编程主要关注客户端与服务端交互的四个问题&#xff1a; 1、连接建立 2、消息到达 3、…

《不要挑战人性》笔记(一)

恒河猴实验 代母实验 将刚出生的小猴子与母亲分开&#xff0c;让它与绒布妈妈跟铁丝妈妈生活在一起。铁丝妈妈身上有食物&#xff0c;绒布没有食物&#xff0c;小猴子更喜欢绒布妈妈&#xff0c;即使它在铁丝妈妈那里得到了食物。绒布妈妈身上设置机关&#xff0c;攻击小猴子&a…

OAuth2 工作流程详解

我们之前谈到了使用saml作为SSO单点登录认证。本文讲解oauth2.0协议&#xff0c;oauth2.0协议避免了客户端直接访问受保护资源 什么是OAuth OAuth是一种安全的开放协议&#xff0c;用于在不相关的服务之间授权用户。换句话说&#xff0c;它使一个服务能够访问托管在其他服务上…

Flutter 笔记 | GetX

官网&#xff1a;https://pub.dev/packages/get 中文文档&#xff1a;https://github.com/jonataslaw/getx/blob/master/README.zh-cn.md 关于 GetX 现在Flutter的状态管理方案很多&#xff0c;redux、bloc、state、provider、Getx。 provider是官方提供的状态管理解决方案&…

小程序分包详解

一&#xff1a;什么是小程序分包 分包指的是把一个完整的小程序项目&#xff0c;按照需求划分为不同的子包&#xff0c;在构建时打包成不同的分包&#xff0c;用户在使用时按需进行加载。 二&#xff1a;为什么使用分包 小程序第一版 推出 小程序代码包不能超过1MB 大小&…

java SSM 教代会审批管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 教代会管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码 和数据库&#xff0c;系统主要采用…

计算机网络开荒2-应用层

文章目录 一、网络应用体系结构2.1 客户机/服务器结构(Client-Server,C/S)2.2 点对点结构(Peer-to-peer,P2P)2.3 混合结构(Hybrid) 二、网路应用的进程通信2.1 套接字Socket2.2 应用层协议的内容 三、网络应用的需求与传输层服务四、Web4.1 HTTP4.1.1 HTTP连接类型4.1.1.1 非持…

mysql慢sql优化实战:in order by优化处理

背景 线上查询慢的问题日益突出,专门写一个帖子记录一下处理过程,会定期更新优化处理方案 套餐余量统计查询菜单,数据库查询时间一分钟五十秒,优化之后耗时109毫秒,性能提升很大.所有时间统计均以数据库层面进行统计.用户使用层面因为有数据传输、带宽、业务逻辑处理等因素…

只要7分钟,教你从0-1搭建BI系统,这份大佬整理的指南免费送!

在BI行业干了快十年了&#xff0c;做过制造业供应链的BI项目&#xff0c;也跟过零售电商、房地产、银行的BI建设&#xff0c;经常有朋友咨询关于BI建设的内容&#xff0c;今天总结了一套BI项目建设指南&#xff0c;直接分享给大家&#xff0c;觉得不错记得点赞收藏。 通常情况…

实战【金融评分卡】

金融评分卡 网站&#xff1a;百融、同盾 1.导入 信贷评分卡是一种用于评估个人或企业申请贷款的工具&#xff0c;就像-个评分表-样。我们可以把它看作是银行或金融机构用来判断某人是否有资格获得贷款的一-种方式。 想象一下,你是一个银行家,有很多人向你申请贷款,比如个人贷…

cas单点登录-服务端部署

一.需求描述 公司开发系统越来越多&#xff0c;每个系统都有自己的登录认证流程&#xff0c;给用户很繁琐的体验&#xff0c;统一认证单点登录迫在眉睫 二.流程图 三.本地运行cas服务端 1.拉取cas服务端代码&#xff0c;切换到对应分支&#xff08;我使用的是6.4.x&#xff…

proxmox7.4 安装后配置

关于proxmox7安装和配置的博文&#xff0c;倒是不少。可惜鱼龙混杂&#xff0c;踩了不少坑&#xff0c;如今实践成功后&#xff0c;做一分享。 proxmox7.4安装(与安装linux系统类似) 1.去官网https://proxmox.com/en/downloads 下载安装镜像&#xff0c;注意&#xff1a; 下…

iTOP-3588开发板Android12源码定制开发uboot开发

uboot 开发-Uboot 源码是 v2017.09 版本。目前在该平台上已经支持 RK 所有主流在售芯片。支持 的功能主要有:  支持 RK Android 固件启动;  支持 Android AOSP 固件启动;  支持 Linux Distro 固件启动;  支持 Rockchip miniloader 和 SPL/TPL 两种 Pre-loader 引导…

对比才知差距!海尔“精华洗”用实验告诉用户

《怪诞关系学》一书中讲到&#xff0c;人生来会与他人做比较&#xff0c;对比是天性&#xff0c;竞争是必然。用高深点的话讲&#xff0c;人是通过对比身边的事物和同类来定位自身的&#xff0c;只有对比才知道差距有多大。“仰观宇宙之大&#xff0c;俯察品类之盛”&#xff0…

Python如何批量合并70个doc和docx文件

目录 一、问题的提出 二、算法分析 三、代码展示 四、注意事项 有一位朋友发来70多个数名命名的doc和docx文件&#xff0c;问我有没有vba代码&#xff0c;可以把这些文件按照数字大小的顺序合并。我试着用了chatgpt&#xff0c;搞了一串vba代码行不通&#xff0c;于时我就找…

【PyQt5】(01)PyQt的详细介绍

文章目录 前言一、PyQt的前世今生1.1 PyQt6、PyQt5和PyQt41.2 商业版和开源版1.3 支持的操作系统 二、 PyQt的优点三、PyQt的应用场景总结 前言 PyQt是使用Python语言编写的QT库的界面工具包。QT是一个跨平台的C应用程序开发框架&#xff0c;可以用于创建高性能、高可靠性、图形…

高频面试八股文用法篇(六) 说说反射及其作用

目录 什么是反射&#xff1f; 反射的作用 反射的优缺点 反射的用途 什么是反射&#xff1f; 动态语言。 当程序运行时&#xff0c;允许改变程序结构或变量类型&#xff0c;这种语言称为动态语言。Java 并不是动态语言&#xff0c;但是它却又一个非常突出的动态相关的机制&am…

Permission denied, please try again.

如果你是容器或者二进制安装启动的gitlab&#xff0c;在克隆代码一直有这样的问题 正克隆到 h5... git192.168.0s password: Permission denied, please try again. 服务ipgit 192.168.40.15 gitlab192.168.40.17 1、 docker run -d --name gitlab -p 80:80 -p 224:22 -p 4…

基于springboot的在线学习平台

源码获取&#xff0c;V&#xff1a;qq2056908377 采用技术 项目整体采用传统的B/S架构和MVC设计模式&#xff0c;开发语言选用JavaHTMLJavascript&#xff0c;技术上采用SpringBoot、Mybatis等开发框架&#xff0c;数据库采用MySQL 5.7。 功能介绍 平台分为用户端和管理两个系…

1.8 基于Java配置方式使用Spring MVC

一、基于Java配置方式使用Spring MVC 实现步骤 1、创建Maven项目 Maven项目 - SpringMvcDemo02 2、添加相关依赖 在pom.xml文件里添加相关依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/…