java并发编程十三 线程池

news2024/11/26 22:34:15

文章目录

  • 线程池
      • 自定义线程池
      • ThreadPoolExecutor
        • 提交任务
        • 关闭线程池
        • 任务调度线程池
        • 正确处理执行任务异常

线程池

自定义线程池

在这里插入图片描述

步骤1:自定义拒绝策略接口

@FunctionalInterface // 拒绝策略
public  interface RejectPolicy<T> {
    void reject(BlockingQueue<T> queue, T task);
}

步骤2:自定义任务队列

@Slf4j
public class BlockingQueue<T> {
    // 1. 任务队列
    private Deque<T> queue = new ArrayDeque<>();
    // 2. 锁
    private ReentrantLock lock = new ReentrantLock();
    // 3. 生产者条件变量
    private Condition fullWaitSet = lock.newCondition();
    // 4. 消费者条件变量
    private Condition emptyWaitSet = lock.newCondition();
    // 5. 容量
    private int capcity;
    public BlockingQueue(int capcity) {
        this.capcity = capcity;
    }
    // 带超时阻塞获取
    public T poll(long timeout, TimeUnit unit) {
        lock.lock();
        try {
            // 将 timeout 统一转换为 纳秒
            long nanos = unit.toNanos(timeout);
            while (queue.isEmpty()) {
                try {
                    // 返回值是剩余时间
                    if (nanos <= 0) {
                        return null;
                    }
                    nanos = emptyWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }
    // 阻塞获取
    public T take() {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                try {
                    emptyWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T t = queue.removeFirst();
            fullWaitSet.signal();
            return t;
        } finally {
            lock.unlock();
        }
    }
    // 阻塞添加
    public void put(T task) {
        lock.lock();
        try {
            while (queue.size() == capcity) {
                try {
                    log.debug("等待加入任务队列 {} ...", task);
                    fullWaitSet.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("加入任务队列 {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
        } finally {
            lock.unlock();
        }
    }
    // 带超时时间阻塞添加
    public boolean offer(T task, long timeout, TimeUnit timeUnit) {
        lock.lock();
        try {
            long nanos = timeUnit.toNanos(timeout);
            while (queue.size() == capcity) {
                try {
                    if(nanos <= 0) {
                        return false;
                    }
                    log.debug("等待加入任务队列 {} ...", task);
                    nanos = fullWaitSet.awaitNanos(nanos);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            log.debug("加入任务队列 {}", task);
            queue.addLast(task);
            emptyWaitSet.signal();
            return true;
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }
    public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
        lock.lock();
        try {
            // 判断队列是否满
            if(queue.size() == capcity) {
                rejectPolicy.reject(this, task);
            } else { // 有空闲
                log.debug("加入任务队列 {}", task);
                queue.addLast(task);
                emptyWaitSet.signal();
            }
        } finally {
            lock.unlock();
        }
    }
}

步骤3:自定义线程池

@Slf4j
public class ThreadPool { // 任务队列
    private BlockingQueue<Runnable> taskQueue;
    // 线程集合
    private HashSet<Worker> workers = new HashSet<>();
    // 核心线程数
    private int coreSize;
    // 获取任务时的超时时间
    private long timeout;
    private TimeUnit timeUnit;
    private RejectPolicy<Runnable> rejectPolicy;
    // 执行任务
    public void execute(Runnable task) {
        // 当任务数没有超过 coreSize 时,直接交给 worker 对象执行
        // 如果任务数超过 coreSize 时,加入任务队列暂存
        synchronized (workers) {
            if(workers.size() < coreSize) {
                Worker worker = new Worker(task);
                log.debug("新增 worker{}, {}", worker, task);
                workers.add(worker);
                worker.start();
            } else {
// taskQueue.put(task);
                // 1) 死等
                // 2) 带超时等待
                // 3) 让调用者放弃任务执行
                // 4) 让调用者抛出异常
                // 5) 让调用者自己执行任务
                taskQueue.tryPut(rejectPolicy, task);
            }
        }
    }
    public ThreadPool(int coreSize, long timeout, TimeUnit timeUnit, int queueCapcity,
                      RejectPolicy<Runnable> rejectPolicy) {
        this.coreSize = coreSize;
        this.timeout = timeout;
        this.timeUnit = timeUnit;
        this.taskQueue = new BlockingQueue<>(queueCapcity);
        this.rejectPolicy = rejectPolicy;
    }
    class Worker extends Thread{
        private Runnable task;
        public Worker(Runnable task) {
            this.task = task;
        }
        @Override
        public void run() {
            // 执行任务
            // 1) 当 task 不为空,执行任务
            // 2) 当 task 执行完毕,再接着从任务队列获取任务并执行
// while(task != null || (task = taskQueue.take()) != null) {
            while(task != null || (task = taskQueue.poll(timeout, timeUnit)) != null) {
                try {
                    log.debug("正在执行...{}", task);
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    task = null;
                }
            }
            synchronized (workers) {
                log.debug("worker 被移除{}", this);
                workers.remove(this);
            }
        }
    }

}

步骤4:测试

 public static void main(String[] args) {
    ThreadPool threadPool = new ThreadPool(1,
            1000, TimeUnit.MILLISECONDS, 1, (queue, task)->{
        // 1. 死等
// queue.put(task);
        // 2) 带超时等待
// queue.offer(task, 1500, TimeUnit.MILLISECONDS);
        // 3) 让调用者放弃任务执行
// log.debug("放弃{}", task);
        // 4) 让调用者抛出异常
// throw new RuntimeException("任务执行失败 " + task);
        // 5) 让调用者自己执行任务
        task.run();
    });
    for (int i = 0; i < 4; i++) {
        int j = i;
        threadPool.execute(() -> {
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("{}", j);
        });
    }
 }

ThreadPoolExecutor

在这里插入图片描述

线程池状态
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量

状态名高 3 位接收新任务处理阻塞队列任务说明
RUNNING111YY
SHUTDOWN000NY不会接收新任务,但会处理阻塞队列剩余任务
STOP001NN会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING010任务全执行完毕,活动线程为 0 即将进入终结
TERMINATED011终结状态

从数字上比较,TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING
这些信息存储在一个原子变量 ctl 中,目的是将线程池状态与线程个数合二为一,这样就可以用一次 cas 原子操作进行赋值

// c 为旧值, ctlOf 返回结果为新值
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))));
 // rs 为高 3 位代表线程池状态, wc 为低 29 位代表线程个数,ctl 是合并它们
private static int ctlOf(int rs, int wc) { return rs | wc; }

构造方法

public ThreadPoolExecutor(int corePoolSize,
 int maximumPoolSize,
 long keepAliveTime,
 TimeUnit unit,
 BlockingQueue<Runnable> workQueue,
 ThreadFactory threadFactory,
 RejectedExecutionHandler handler)
  • corePoolSize 核心线程数目 (最多保留的线程数)
  • maximumPoolSize 最大线程数目
  • keepAliveTime 生存时间 - 针对救急线程
  • unit 时间单位 - 针对救急线程
  • workQueue 阻塞队列
  • threadFactory 线程工厂 - 可以为线程创建时起个好名字
  • handler 拒绝策略

工作方式:
在这里插入图片描述

  • 线程池中刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
  • 当线程数达到 corePoolSize 并没有线程空闲,这时再加入任务,新加的任务会被加入workQueue 队列排队,直到有空闲的线程。
  • 如果队列选择了有界队列,那么任务超过了队列大小时,会创建 maximumPoolSize - corePoolSize 数目的线
    程来救急。
  • 如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。拒绝策略 jdk 提供了 4 种实现,其它著名框架也提供了实现
    • AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略
      CallerRunsPolicy 让调用者运行任务
    • DiscardPolicy 放弃本次任务
    • DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
    • Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方便定位问题
    • Netty 的实现,是创建一个新线程来执行任务
    • ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
    • PinPoint 的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略
  • 当高峰过去后,超过corePoolSize 的救急线程如果一段时间没有任务做,需要结束节省资源,这个时间由keepAliveTime 和 unit 来控制。
    在这里插入图片描述

根据这个构造方法,JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池
newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
 return new ThreadPoolExecutor(nThreads, nThreads,
 0L, TimeUnit.MILLISECONDS,
 new LinkedBlockingQueue<Runnable>());
 }

特点

  • 核心线程数 == 最大线程数(没有救急线程被创建),因此也无需超时时间
  • 阻塞队列是无界的,可以放任意数量的任务
    newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
 60L, TimeUnit.SECONDS,
 new SynchronousQueue<Runnable>());
 }

特点

  • 核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s,意味着
    • 全部都是救急线程(60s 后可以回收)
    • 救急线程可以无限创建
  • 队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)
    SynchronousQueue<Integer> integers = new SynchronousQueue<>();
new Thread(() -> {
        try {
            log.debug("putting {} ", 1);
            integers.put(1);
            log.debug("{} putted...", 1);
            log.debug("putting...{} ", 2);
            integers.put(2);
            log.debug("{} putted...", 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },"t1").start();
    sleep(1);
new Thread(() -> {
        try {
            log.debug("taking {}", 1);
            integers.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },"t2").start();
    sleep(1);
new Thread(() -> {
        try {
            log.debug("taking {}", 2);
            integers.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    },"t3").start();

输出

11:48:15.500 c.TestSynchronousQueue [t1] - putting 1  
11:48:16.500 c.TestSynchronousQueue [t2] - taking 1 
11:48:16.500 c.TestSynchronousQueue [t1] - 1 putted... 
11:48:16.500 c.TestSynchronousQueue [t1] - putting...2  
11:48:17.502 c.TestSynchronousQueue [t3] - taking 2 
11:48:17.503 c.TestSynchronousQueue [t1] - 2 putted... 

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
 return new FinalizableDelegatedExecutorService
        (new  ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
 new LinkedBlockingQueue<Runnable>()));
}

使用场景:
希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。
区别:

  • 自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一个线程,保证池的正常工作
  • Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
    • FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法
  • Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改
    • 对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用 setCorePoolSize 等方法进行修改
提交任务
// 执行任务
void execute(Runnable command);
 // 提交任务 task,用返回值 Future 获得任务执行结果
<T> Future<T> submit(Callable<T> task);
 // 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
 throws InterruptedException;
 // 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
 long timeout, TimeUnit unit)
 throws InterruptedException;
 // 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
 throws InterruptedException, ExecutionException;
 // 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
 long timeout, TimeUnit unit)
 throws InterruptedException, ExecutionException, TimeoutException;
关闭线程池

shutdown

 /*
线程池状态变为 SHUTDOWN- 不会接收新任务- 但已提交任务会执行完- 此方法不会阻塞调用线程的执行
*/
 void shutdown();
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 修改线程池状态
            advanceRunState(SHUTDOWN);
            // 仅会打断空闲线程
            interruptIdleWorkers();
            onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
        tryTerminate();
    }

shutdownNow

 /*
线程池状态变为 STOP- 不会接收新任务- 会将队列中的任务返回- 并用 interrupt 的方式中断正在执行的任务
*/
 List<Runnable> shutdownNow();
public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 修改线程池状态
            advanceRunState(STOP);
            // 打断所有线程
            interruptWorkers();
            // 获取队列中剩余任务
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        // 尝试终结
        tryTerminate();
        return tasks;
    }

其它方法

// 不在 RUNNING 状态的线程池,此方法就返回 true
 boolean isShutdown();
 // 线程池状态是否是 TERMINATED
 boolean isTerminated();
 // 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事情,可以利用此方法等待
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
任务调度线程池

在『任务调度线程池』功能加入之前,可以使用 java.util.Timer 来实现定时功能,Timer 的优点在于简单易用,但由于所有任务都是由同一个线程来调度,因此所有任务都是串行执行的,同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任务。

public static void main(String[] args) {
 Timer timer = new Timer();
 TimerTask task1 = new TimerTask() {
 @Override
 public void run() {
 log.debug("task 1");
 sleep(2);
       }
    };
 TimerTask task2 = new TimerTask() {
 @Override
 public void run() {
 log.debug("task 2");
        }
    };
 // 使用 timer 添加两个任务,希望它们都在 1s 后执行
// 但由于 timer 内只有一个线程来顺序执行队列中的任务,因此『任务1』的延时,影响了『任务2』的执行
timer.schedule(task1, 1000);
 timer.schedule(task2, 1000);
 }

输出

20:46:09.444 c.TestTimer [main] - start... 
20:46:10.447 c.TestTimer [Timer-0] - task 1 
20:46:12.448 c.TestTimer [Timer-0] - task 2 

使用 ScheduledExecutorService 改写:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
 // 添加两个任务,希望它们都在 1s 后执行
executor.schedule(() -> {
 System.out.println("任务1,执行时间:" + new Date());
 try { Thread.sleep(2000); } catch (InterruptedException e) { }
 }, 1000, TimeUnit.MILLISECONDS);
 executor.schedule(() -> {
 System.out.println("任务2,执行时间:" + new Date());
 }, 1000, TimeUnit.MILLISECONDS);

输出

任务1,执行时间:Thu Jan 03 12:45:17 CST 2019 
任务2,执行时间:Thu Jan 03 12:45:17 CST 2019 
正确处理执行任务异常

方法1:主动捉异常

ExecutorService pool = Executors.newFixedThreadPool(1);
 pool.submit(() -> {
 try {
 log.debug("task1");
 int i = 1 / 0;
    } 
catch (Exception e) {
 log.error("error:", e);
    }
 });

输出

21:59:04.558 c.TestTimer [pool-1-thread-1] - task1 
21:59:04.562 c.TestTimer [pool-1-thread-1] - error: 
java.lang.ArithmeticException: / by zero 
at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) 
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
at java.lang.Thread.run(Thread.java:748) 

方法2:使用 Future

 ExecutorService pool = Executors.newFixedThreadPool(1);
 Future<Boolean> f = pool.submit(() -> {
 log.debug("task1");
 int i = 1 / 0;
 return true;
 });
 log.debug("result:{}", f.get());

输出

21:54:58.208 c.TestTimer [pool-1-thread-1] - task1 
Exception in thread "main" java.util.concurrent.ExecutionException: 
java.lang.ArithmeticException: / by zero 
at java.util.concurrent.FutureTask.report(FutureTask.java:122) 
at java.util.concurrent.FutureTask.get(FutureTask.java:192) 
at cn.itcast.n8.TestTimer.main(TestTimer.java:31) 
Caused by: java.lang.ArithmeticException: / by zero 
at cn.itcast.n8.TestTimer.lambda$main$0(TestTimer.java:28) 
at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 
at java.lang.Thread.run(Thread.java:748) 

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

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

相关文章

LeetCode---120双周赛

题目列表 2970. 统计移除递增子数组的数目 I 2971. 找到最大周长的多边形 2972. 统计移除递增子数组的数目 II 2973. 树中每个节点放置的金币数目 一、统计移除递增子数组的数目I 这题的数据范围不高&#xff0c;可以直接暴力&#xff0c;后面的第三题和它一样&#xff0c…

程序员面试笔试通关宝典系列丛书(由清华大学出版社出版)

程序员面试笔试通关宝典系列——编程职场成功的必备秘籍 由清华大学出版社出版的专为编程爱好者和职业开发者打造的“程序员面试笔试通关宝典”系列丛书。该系列包含五本专业指南&#xff0c;覆盖数据库、Java、前端、通用编程和Python五个领域。 这些书籍深度解析各领域的核…

面试题:MySQL 自增主键一定是连续的吗?

文章目录 测试环境&#xff1a;一、自增值的属性特征&#xff1a;1. 自增主键值是存储在哪的&#xff1f;2. 自增主键值的修改机制&#xff1f; 二、新增语句自增主键是如何变化的&#xff1a;三、自增主键值不连续情况&#xff1a;&#xff08;唯一主键冲突&#xff09;四、自…

AIGC开发:调用openai的API接口

简介 开始进行最简单的使用&#xff1a;通过API调用openai的模型能力 OpenAI的能力如下图&#xff1a; 文本生成模型 OpenAI 的文本生成模型&#xff08;通常称为生成式预训练 Transformer 或大型语言模型&#xff09;经过训练可以理解自然语言、代码和图像。这些模型提供文…

扫雷(c语言)

先开一个test.c文件用来游戏的逻辑测试&#xff0c;在分别开一个game.c文件和game.h头文件用来实现游戏的逻辑 主要步骤&#xff1a; 游戏规则&#xff1a; 输入1&#xff08;0&#xff09;开始&#xff08;结束&#xff09;游戏&#xff0c;输入一个坐标&#xff0c;如果该坐…

新药(化药)注册申报资料都包含哪些?

新药的注册申报是新药上市前的重要步骤,其流程可以简单概括为①前期准备→②申报材料准备→③递交注册申请→④审评和审批→⑤监管和跟踪。本文将着重介绍新药(化药)注册申报的一般流程和主要环节(附流程图)。(关于新药注册申报成功率和耗费时间问题写到最后) ①前期准备 在开…

年底医保新改革,如何短,平,快搞定his医保接口升级

1. 先说说我负责运维的医院背景&#xff1a; 省会中心城市三甲&#xff0c;电脑共计1500台左右。其中安装有his系统的电脑大约600台&#xff0c;也就是说需要给600台医护人员使用电脑进行医保接口升级。 近期有幸参与了院里HIS系统的升级工作&#xff0c;赶上特殊时期&#x…

数字身份验证:跨境电商如何应对账户安全挑战?

在数字化时代&#xff0c;随着跨境电商的蓬勃发展&#xff0c;账户安全问题逐渐成为行业和消费者关注的焦点。随着网络犯罪日益猖獗&#xff0c;用户的数字身份安全面临着更加复杂的威胁。本文将深入探讨数字身份验证在跨境电商中的重要性&#xff0c;并探讨各种创新技术和策略…

SQL Server 索引和视图

CSDN 成就一亿技术人&#xff01; 难度指数&#xff1a;* * * CSDN 成就一亿技术人&#xff01; 目录 1.索引 什么是索引&#xff1f; 索引的作用&#xff1f; 索引的分类 1. 唯一索引 2. 主键索引 3. 聚集索引 4.非聚集索引 5.复合索引 6.全文搜索 索引的创建&am…

docker-compose部署zabbix服务

1.首先要有docker环境&#xff0c; 关闭防火墙&#xff0c;selinux 开启docker&#xff0c;并设置开机自启动 Linux的docker的安装https://blog.csdn.net/m0_58146415/article/details/134654933 2.docker-compose的安装----github下载 curl -SL https://github.com/docke…

Unity Shader-真实下雨路面

Unity Shader-真实下雨路面 简介素材1.准备插件Amplify Shader Editor&#xff08;这里我使用的是1.6.4最新版&#xff09;2.贴纸和切图d 一、创建一个Shader Surface&#xff0c;实现气泡播放效果二、叠加一次气泡播放效果&#xff0c;使其看起来更多&#xff0c;更无序三、小…

【C++入门(一)】:详解C++语言的发展及其重要性

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; C入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f324;️什么是C&#x1f324;️C的发展史&#x1f324;️C的重要性☁️语言的广泛度☁️C的领域⭐…

鸿蒙系列--组件介绍之容器组件

一、Badge 描述&#xff1a;给其他组件添加标记 子组件&#xff1a;支持单个子组件 1.创建数字标记 Badge(value: {count: number, position?: BadgePosition, maxCount?: number, style: BadgeStyle}) 2.创建字符串标记 Badge(value: {value: string, position?: Badge…

62元一年的腾讯云轻量2核2G3M服务器性能测评

阿里云轻量应用服务器2核2G3M带宽优惠价格62元一年&#xff0c;100%CPU性能&#xff0c;3M带宽下载速度384KB/秒&#xff0c;40GB SSD系统盘&#xff0c;月流量200GB&#xff0c;折合每天6.6GB流量&#xff0c;超出月流量包的流量按照0.8元每GB的价格支付流量费&#xff0c;地域…

算法基础day2

前缀和 #include <iostream> using namespace std; const int N100010; int n,m; int a[N],s[N]; int main() {scanf("%d%d",&n,&m);for(int i1;i<n;i) scanf("%d",&a[i]);for(int i1;i<n;i) s[i]s[i-1]a[i];while(m--){int l,r;s…

鸿鹄电子招投标系统:基于Spring Boot、Mybatis、Redis和Layui的企业电子招采平台源码与立项流程

在数字化时代&#xff0c;企业需要借助先进的数字化技术来提高工程管理效率和质量。招投标管理系统作为企业内部业务项目管理的重要应用平台&#xff0c;涵盖了门户管理、立项管理、采购项目管理、采购公告管理、考核管理、报表管理、评审管理、企业管理、采购管理和系统管理等…

单机+内部备份_全备案例

此场景为单机数据库节点内部备份&#xff0c;方便部署和操作&#xff0c;但备份REPO与数据库实例处于同一个物理主机&#xff0c;冗余度较低。 前期准备 配置ksql免密登录(必须) 在Kingbase数据库运行维护中&#xff0c;经常用到ksql工具登录数据库&#xff0c;本地免密登录…

将H5封装为App:实现跨平台移动应用开发的新趋势

H5技术指的是HTML5、CSS3和JavaScript等一系列前端技术的综合应用。它具有跨平台、开发成本低、开发周期短等优势&#xff0c;可以快速实现丰富的界面和交互效果。而原生应用开发受限于操作系统的差异&#xff0c;需要分别开发不同平台的应用&#xff0c;这就增加了开发成本和工…

车联网的安全风险与应对措施

安全风险 1、恶意软件 攻击者可以通过入侵厂商或供应商网络&#xff0c;用恶意软件&#xff08;如病毒、木马、勒索软件等&#xff09;感染车联网系统组件&#xff0c;从而获得对车辆的控制权或窃取敏感信息。例如&#xff0c;一名安全研究人员成功入侵了特斯拉&#xff08;T…