java并发编程 JUC-基础篇 快速入门

1.进程与线程的概念

(1)进程

程序有指令与数据组成,指令要运行,数据要读写,就必须指令加载到CPU。数据加载到内容,指令运行需要用到磁盘。

当一个程序被运行时,从磁盘加载这个程序的代码至内存,这时开启了一个进程。

(2)线程

一个进程之内可以分为一个或多个线程。

一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行。

Java中的 最小调度单位是线程,最小资源分配单位是进程。

2.并发与并行的概念

并发:同一时间应对多件事情的能力。

并行:同一时间动手做多件事情的能力。

3.异步与同步的概念

异步:不需要等待结果返回,就能继续运行

同步:需要等待结果返回,才能继续运行

同步在多线程中是让多个线程步调一致

4.多线程提高效率的结论

(1)单核CPU下,多线程不能实际提高程序运行效率,只是为了能够在不同任务之间切换,不同

线程轮流使用CPU,不至于一个线程总是占用一个CPU,别的线程没法干活。

(2)多核CPU下,可以并行运行多个线程,能否提高效率要分情况:

一些任务可以通过设计,将任务拆分,并行执行,可以提高运行效率。

不是所有任务都需要拆分,任务的目的不同。

(3)IO操作不占用CPU,只是我们一般拷贝文件使用的是 阻塞IO,相当于线程虽然不用CPU,但是需要等待IO结束,没能充分利用线程,才有了 非阻塞IO异步IO 优化

5.创建线程

5.1 Thread的方式

        Thread thread = new Thread(){@Overridepublic void run(){System.out.println("Hello JUC");}};thread.start();

5.2 Runnable的方式

        Runnable runnable = new Runnable(){@Overridepublic void run(){System.out.println("Hello JUC Runnable");}};Thread thread = new Thread(runnable);thread.start();

5.3 Lambda的方式

5.3.1 Thread的Lambda写法

        Thread thread = new Thread(()->{ System.out.println("Hello JUC Lambda");});thread.start();

5.3.2 Runnable的Lambda写法

        Runnable runnable = ()->{System.out.println("Hello JUC Lambda");};Thread thread = new Thread(runnable);thread.start();

5.4 FutureTaks 配合 Thread

FutureTasks 能够接收Callable类型的参数,用于·处理有返回结果的情况

    @Testvoid testFutureTask() throws ExecutionException, InterruptedException {FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {System.out.println("Running ...");Thread.sleep(3000);return 100;}});Thread thread = new Thread(task);thread.start();System.out.println(task.get());}

6.线程运行

线程运行现象:线程运行交替执行。谁先谁后,不由我们所控制。

查询线程的方式:

(1)windows:

可以通过任务管理器查看进程和线程数

tasklilst 查看进程

taskkill 杀死进程

(2)linux:

ps -fe 查看所有进程

ps -fT -p <PID> 这将显示PID进程及其所有线程的详细信息。

kill 杀死进程

top 按大写H切换是否显示线程

top -H -p <PID> 查看某个进程(PID)

(3)Java:

jps 命令查看所有Java进程

jstack <PID> 查看某个Java进程(PID)的所有线程状态

jconsole 来查看某个Java进程中线程的运行情况

7.线程运行的原理

7.1栈帧

JVM是由堆、栈、方法区所组成的,其中栈内存 存放就是线程,每个线程启动后,虚拟机就会分配一块栈内存。

每个栈有多个栈帧,对应着每次方法调用时所占用的内存。

每个线程只有一个活动栈帧,对应着当前正在执行的那个方法。

7.2上下文切换(Thread Context Switch)

可能因为

  • 线程的cpu时间用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了sleep、yield、wait、join、park、synchronized、lock等方法

当Thread Context Switch发送时,需要操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用记录下一条jvm指令的执行地址,是线程私有的。

状态包括程序计数器,虚拟机中每个栈帧的信息,如局部变量,操作数栈,返回地址等

Thread Context Switch频繁发生会影响性能

8.线程中的常见方法

方法描述
start()启动新线程并执行该线程的run()方法。线程开始执行时,它的run()方法会被调用。
run()当线程启动时,run()方法会被调用。这个方法应该包含线程的执行代码。
join()等待调用join()方法的线程终止。例如,thread.join()会使当前线程等待thread线程终止。
sleep(long millis)使当前线程暂停执行指定的毫秒数。线程不会失去任何监视器的所有权。
interrupt()中断线程。如果线程在调用Object类的wait()wait(long)wait(long, int)join()join(long, int)或者sleep(long, int)方法时被阻塞,那么它的中断状态将被清除,并且它将接收到InterruptedException
isAlive()测试线程是否处于活动状态。如果线程已经启动且尚未终止,则线程处于活动状态。
setName(String name)改变线程的名称,可以通过getName()方法获取线程的名称。
getPriority()返回线程的优先级。线程的优先级可以设置为MIN_PRIORITY(1)、NORM_PRIORITY(5)或MAX_PRIORITY(10)。
setPriority(int newPriority)变线程的优先级。线程优先级只是建议给调度器,实际调度可能会忽略它。
yield()暂停当前正在执行的线程对象,并执行其他线程。
getState()返回线程的状态。线程的状态可以是NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED
isInterrupted()测试线程是否已经中断。不同于interrupted()方法,这个方法不会改变线程的中断状态。

8.1 run 与 start 的区别

(1)run() 方法:

  • run() 方法是线程的执行主体,它包含了线程要执行的任务。
  • 当线程被启动时,run() 方法会被自动调用。
  • run() 方法可以直接调用,就像调用普通方法一样,它在当前线程中执行,而不是在新线程中执行。
  • 如果直接调用 run() 方法,程序不会创建新的线程,而是在当前线程中顺序执行 run() 方法中的代码。

(2)start() 方法:

  • start() 方法用于启动一个新线程,并执行该线程的 run() 方法。
  • 当 start() 方法被调用时,Java 虚拟机会创建一个新的线程,并执行 run() 方法中的代码。
  • start() 方法只能被调用一次,多次调用会抛出 IllegalThreadStateException
  • 调用 start() 方法后,线程可能会立即开始执行,也可能因为线程调度器的安排而在稍后执行。

8.2 Sleep和yield的区别

(3)Sleep

  • 调用sleep让当前线程从Running进入Timed Waiting(阻塞)状态
  • 其他线程可以通过interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
  • 睡眠解释后的线程未必会立即执行
  • 建议使用Timeunit的sleep代替Thread的sleep,有更好的可读性

(4)yield

  • 调用yield 会使当前线程从Running进入Runnable就绪状态,然后调度执行其他线程
  • 具体的实现依赖操作系统的任务调度器
  • 在Java中,Thread.yield()也是一个静态方法,它使当前线程从运行状态转到可运行状态,但不会释放所占有的任何资源。
  • 通常,yield的使用并不频繁,因为它对线程调度提供的信息有限,且线程调度器可能会忽略这个提示。

8.3 线程优先级

  • 线程优先级会提示调度器优先调度线程
  • 如果CPU比较忙,那么优先级高的线程会获得更多的时间片;如果CPU比较空闲,优先级几乎没有什么作用。

8.4 Sleep实现

在没有利用CPU实现计算时,不要让while(true)空转浪费CPU,这时可以使用yield或sleep使CPU的使用权让给其他的程序

8.5 join实现

等待thread线程运行结束或终止。

如果join(long n)带参数的话,就是等待线程运行结束的最多等待n毫秒

8.6 打断阻塞

阻塞

打断sleep的线程,清空打断状态

示例:

  @Testvoid test() throws ExecutionException, InterruptedException {Thread t1 = new Thread(()->{try{TimeUnit.SECONDS.sleep(12);}catch (Exception e){e.printStackTrace();}},"t1");t1.start();TimeUnit.SECONDS.sleep(2);t1.interrupt();System.out.println(t1.isInterrupted());}

注意: 使用 isInterrupted()来判断是否打断成功,对于打断sleep的结果为false则表示打断成功,同理wait和join都是false标记为打断成功。

8.7 打断正常

打断正常的则isInterrupted()为true则表示打断成功

示例:

    @Testvoid test() throws  InterruptedException {Thread t1 = new Thread(()->{try{while (true){boolean interrupted = Thread.currentThread().isInterrupted();if (interrupted){log.info("打斷...");break;}}}catch (Exception e){e.printStackTrace();}},"t1");t1.start();TimeUnit.SECONDS.sleep(3);log.info("interrupt");t1.interrupt();}

9.两阶段终止

问题:在一个线程T1中如何优雅的终止线程T2呢?

错误的思路:

  • 使用stop方法终止线程,这种如果此时线程锁住了公共享资源,将其杀死后就再也没有机会释放资源,导致其他线程永远无法获取锁。
  • 使用System.exit(int)方法停止线程,目的是仅停止一个线程,但是会让整个程序都停止

正确的思路:

    @Testvoid test() throws  InterruptedException {Thread t1 = new Thread(()->{while (true){Thread current = Thread.currentThread();if(current.isInterrupted()){log.info("打断....");break;}try {Thread.sleep(800);log.info("正在记录中.....");}catch (InterruptedException e){e.printStackTrace();//重置设置打断标识,因为sleep过程中进行interrupt是标识false,那么需要正常的interrupt使其标识为truecurrent.interrupt();}}});t1.start();TimeUnit.SECONDS.sleep(3);t1.interrupt();}

10.打断park线程

打断线程后不会影响标记

11.不推荐的方法

这些方法过时,容易破坏同步代码块,造成线程死锁

方法名说明
stop()停止线程运行
suspend()挂起(暂停)线程运行
resume()恢复线程运行

12.主线程与守护线程

默认情况下,Java进程需要等待线程运行都结束,才会结束。有一种特殊的线程是守护线程,只要其他的线程运行结束了,即使守护线程的代码还没有执行结束,也会强制结束。

示例:设置t1线程为守护线程:

t1.setDaemon(true);

运用:

  • 垃圾回收器线程就是一种守护线程
  • 接收shutdown命令后,不会等待Acceptor和Poller守护线程处理完当前请求

13.线程的五种状态

五种状态:初始状态、可运行状态、运行状态、终止状态、阻塞状态

  • 初始状态:仅在语言层面创建了线程对象,还未与操作系统线程关联
  • 可运行状态;也称(就绪状态),指线程已经被创建(与操作系统线程未关联),可以由CPU调度执行
  • 运行状态:指获取CPU时间片运行中的状态
  • 阻塞状态:如果调用了阻塞API,这时线程实际不会用到CPU,会导致线程上下文切换,进入阻塞状态
  • 终止状态:表示线程已经执行完毕,生命周期已经结束,不会转换为其他状态


14.共享资源的线程安全(synchronized)

竞态条件:发生在至少两个线程竞争同一资源时,而最终的结果取决于这些线程的执行顺序。在大多数情况下,竞态条件的出现是由于程序设计上的缺陷,例如没有适当的同步机制来控制对共享资源的访问。

为了避免临界的竞态的条件发生,可以使用以下的手段实现:

  • 阻塞式的解决方案:Synchronized,Lock
  • 非阻塞式的解决方案:原子变量

使用阻塞式的解决方案:Synchronized(对象锁),采用互斥的方式让同一时刻至多只有一个线程能持有 对象锁,其他线程再想获取这个 对象锁 就会被阻塞住。这样可以保证线程可以安全的执行临界区内的代码,不用担心线程的上下文切换

  • 互斥是保证临界区的竞态条件发生,同一时刻只有一个线程执行临界区代码
  • 同步是由于线程执行的先后,顺序不同,需要一个线程等待其他线程运行到某个点

 示例代码:

    int counter = 0;Object lock = new Object();@Testvoid test() throws  InterruptedException {Thread t1 = new Thread(()->{for (int i = 0;i<=50000;i++){synchronized(lock){counter++;}}});Thread t2 = new Thread(()->{for (int i = 0;i<=50000;i++){synchronized(lock){counter--;}}});t1.setName("t1");t2.setName("t2");t1.start();t2.start();t1.join();t2.join();log.info("Counter:{}",counter);}

synchronized用对象锁保证了临界区内代码的原子性,临界区的代码对外不可分割,不会被线程切换所打断

思考:

如果synchronized(obj)放在for循环外面,如何理解? --原子性

如果 t1 synchronized(obj1) 而 t2 synchronized(obj2) 会怎么运行?--锁对象

如果 t1 synchronized(obj)而 t2没有加会怎么样?--锁对象

14.1 通过面向对象的方式实现:

创建实体:

public class Room {private int counter = 0;public void increment(){synchronized (this){counter++;}}public void decrement(){synchronized (this){counter--;}}public int getCounter(){synchronized (this){return counter;}}
}

实现代码:

    Room room = new Room();@Testvoid test() throws  InterruptedException {Thread t1 = new Thread(()->{room.increment();});Thread t2 = new Thread(()->{room.decrement();});t1.setName("t1");t2.setName("t2");t1.start();t2.start();t1.join();t2.join();log.info("Counter:{}",room.getCounter());}

      14.2 synchronized加在方法上

(1)

public class Test {public synchronized void test(){}}

相当于:

public class Test {public  void test(){synchronized(this){}}}

(2)static方法

public class Test {public synchronized static void test(){}}

相当于:

public class Test {public  static void test(){synchronized(Test.class){}}}

15.变量线程安全分析

15.1 成员变量和静态变量是否线程安全?

(1)如果没有共享,则线程安全

(2)如果共享,根据他们的状态是否能够改变,分为两种情况:

  • 如果只有读操作,则线程安全
  • 如果有读写操作,则这段代码是临界区,需要考虑线程安全

15.2 局部变量是否线程安全?

(1)局部变量是线程安全的

(2)但是局部变量引用的对象则未必

  • 如果该对象没有逃离方法的作用访问,它是线程安全的
  • 如果该对象逃离方法的作用范围,则需要考虑线程安全

16.常用的线程安全类

  • String
  • Integer
  • StringBuffer
  • Random
  • Vector
  • Hashtable
  • java.utils.concurrent包下的类

这里的线程安全是多线程调用它们同一个实例的某个方法时,线程是安全的。可以理解为

  • 它们的每个方法是原子
  • 但是它们的多个方法的组合不是原子的(不是线程安全的)

不可改变线程安全

String、Integer等都是不可变类,因为其内部的状态不可改变,因此它们的方法都是线程安全的

虽然String有replace、substring等方法可以改变值、但是根据源码可知都是创新一个新的对象,没有改变原有的对象的值,所以线程安全

17.买票问题习题

(1)线程安全问题示例

package org.example;import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;// 主类,用于执行售票模拟
public class ExerciseSell {// 执行售票模拟的入口点public static void main(String[] args) throws InterruptedException {// 创建一个 TicketWindow 实例,初始票数为1000TicketWindow ticket = new TicketWindow(1000);// 创建一个线程列表,用于存放即将创建的线程List<Thread> threadList = new ArrayList<>();// 创建一个Integer类型的列表,用于存放每个线程卖出的票数List<Integer> amountList = new Vector<>();// 创建80000个线程,每个线程将模拟卖出一定数量的票for(int i = 0; i < 80000; i++) {Thread thread = new Thread(() -> {// 每个线程卖出的票数是随机生成的int amount = ticket.sell(randomAmount());try {// 模拟售票操作后的延迟Thread.sleep(10);} catch (InterruptedException e) {// 如果线程在睡眠中被中断,抛出运行时异常throw new RuntimeException(e);}// 将卖出的票数添加到amountList中amountList.add(amount);});// 将新创建的线程添加到线程列表中threadList.add(thread);// 启动线程thread.start();}// 等待所有线程完成for (Thread thread : threadList) {thread.join();}// 打印剩余票数System.out.println("余票:" + ticket.getAmount());// 计算所有线程卖出的票数总和并打印System.out.println("卖出的票数:" + amountList.stream().mapToInt(i -> i).sum());}// 生成随机售票数的方法static Random random = new Random();public static int randomAmount() {// 返回1到5之间的随机整数,包括1和5return random.nextInt(5) + 1;}
}// 表示售票窗口的类
class TicketWindow {// 私有属性,表示售票窗口的票数private int amount;// 构造函数,初始化票数为传入的参数public TicketWindow(int amount) {this.amount = amount;}// 获取当前票数的方法public int getAmount() {return amount;}// 售票方法,尝试从窗口卖出指定数量的票public int sell(int amount) {if (this.amount >= amount) {// 如果票数足够,减少票数并返回卖出的票数this.amount -= amount;return amount;} else {// 如果票数不足,返回0return 0;}}
}

(2)解决线程安全问题方法

这段代码存在线程安全问题。具体来说,TicketWindow 类的 amount 成员变量在被多个线程访问和修改时,没有使用任何同步机制,这可能导致多个线程同时修改 amount,从而引发数据不一致的问题。

所以要给amount 进行共享资源读写的时进行加锁操作(这段代码,只需要对sell方法加上synchronized即可加锁)

 public synchronized int sell(int amount){if(this.amount >= amount){this.amount -= amount;return amount;}else {return 0;}}

18. 转账习题

(1)线程安全问题示例
 

package org.example;import java.util.Random;public class ExerciseTransfer {public static void main(String[] args) throws InterruptedException {Amount a = new Amount(1000);Amount b = new Amount(1000);Thread t1 = new Thread(()->{for(int i = 0;i < 100;i++){a.transfer(b,randomAmount());}});Thread t2 = new Thread(()->{for(int i = 0;i < 100;i++){b.transfer(a,randomAmount());}});t1.start();t2.start();t1.join();t2.join();System.out.println("总金额:"+a.getMoney() + b.getMoney());}static Random random = new Random();public static int randomAmount(){return random.nextInt(100) + 1;}}class Amount{private int money;public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}public Amount(int money) {this.money = money;}public void transfer(Amount amount,int transferMoney){if(this.money >= transferMoney){this.setMoney(this.getMoney() - transferMoney);amount.setMoney(amount.getMoney() + transferMoney);}}
}

(2)解决线程安全问题方法(两个共享变量)

    public void transfer(Amount amount,int transferMoney){synchronized(Amount.class){if(this.money >= transferMoney){this.setMoney(this.getMoney() - transferMoney);amount.setMoney(amount.getMoney() + transferMoney);}}}

 因为如果

public synchronized  void transfer(Amount amount,int transferMoney){if(this.money >= transferMoney){this.setMoney(this.getMoney() - transferMoney);amount.setMoney(amount.getMoney() + transferMoney);}}

相当于保护this.money,但是不保护amount的money

    public void transfer(Amount amount,int transferMoney){synchronized(this){if(this.money >= transferMoney){this.setMoney(this.getMoney() - transferMoney);amount.setMoney(amount.getMoney() + transferMoney);}}}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL篇三:数据类型

文章目录 前言1. 数值类型1.1 tinyint类型1.2 bit类型1.3 小数类型1.3.1 float1.3.2 decimal 2. 字符串类型2.1 char2.2 varchar2.3 char和varchar比较 3. 日期类型4. enum和set 前言 数据类型分类&#xff1a; 1. 数值类型 1.1 tinyint类型 在MySQL中&#xff0c;整型可以指…

如何第一次从零上传项目到GitLab

嗨&#xff0c;我是兰若&#xff0c;今天想给大家说下&#xff0c;如何上传一个完整的项目到与LDAP集成的GitLab&#xff0c;也就是说这个项目之前是不在git上面的&#xff0c;这是第一次上传&#xff0c;这样上传上去之后&#xff0c;其他小伙伴就可以根据你这个项目的git地址…

自动批量将阿里云盘文件发布成WordPress文章脚本源码(以RiPro主题为例含付费信息下载地址SEO等自动设置)源码

背景 很多资源下载站&#xff0c;付费资源下载站&#xff0c;付费内容查看等都可以用WordPress站点发布内容&#xff0c;这些站点一般会基于一个主题&#xff0c;付费信息作为文章附属的信息发布&#xff0c;底层存储在WP表里&#xff0c;比如日主题&#xff0c;子比主题等。 …

2-5 softmax 回归的简洁实现

我们发现通过深度学习框架的高级API能够使实现线性回归变得更加容易。 同样&#xff0c;通过深度学习框架的高级API也能更方便地实现softmax回归模型。 本节如在上节中一样&#xff0c; 继续使用Fashion-MNIST数据集&#xff0c;并保持批量大小为256。 import torch from torc…

【基础篇】第4章 Elasticsearch 查询与过滤

在Elasticsearch的世界里&#xff0c;高效地从海量数据中检索出所需信息是其核心价值所在。本章将深入解析查询与过滤的机制&#xff0c;从基础查询到复合查询&#xff0c;再到全文搜索与分析器的定制&#xff0c;为你揭开数据检索的神秘面纱。 4.1 基本查询 4.1.1 Match查询…

多语言版在线出租车预订完整源码+用户应用程序+管理员 Laravel 面板+ 司机应用程序最新版源码

源码带PHP后台客户端源码 Flutter 是 Google 开发的一款开源移动应用开发 SDK。它用于开发 Android 和 iOS 应用&#xff0c;也是为 Google Fuchsia 创建应用的主要方法。Flutter 小部件整合了所有关键的平台差异&#xff0c;例如滚动、导航、图标和字体&#xff0c;可在 iOS 和…

如何在前端网页实现live2d的动态效果

React如何在前端网页实现live2d的动态效果 业务需求&#xff1a; 因为公司需要做机器人相关的业务&#xff0c;主要是聊天形式的内容&#xff0c;所以需要一个虚拟的卡通形象。而且为了更直观的展示用户和机器人对话的状态&#xff0c;该live2d动画的嘴型需要根据播放的内容来…

昇思25天学习打卡营第08天 | 模型训练

昇思25天学习打卡营第08天 | 模型训练 文章目录 昇思25天学习打卡营第08天 | 模型训练超参数损失函数优化器优化过程 训练与评估总结打卡 模型训练一般遵循四个步骤&#xff1a; 构建数据集定义神经网络模型定义超参数、损失函数和优化器输入数据集进行训练和评估 构建数据集和…

【Excel、RStudio计算T检测的具体操作步骤】

目录 一、基础知识1.1 显著性检验1.2 等方差T检验、异方差T检验1.3 单尾p、双尾p1.3.1 检验目的不同1.3.2 用法不同1.3.3 如何选择 二、Excel2.1 统计分析工具2.1.1 添加统计分析工具2.1.2 数据分析 2.2 公式 -> 插入函数 -> T.TEST 三、RStudio 一、基础知识 参考: 1.…

无人机运营合格证及无人机驾驶员合格证(AOPA)技术详解

无人机运营合格证及无人机驾驶员合格证&#xff08;AOPA&#xff09;技术详解如下&#xff1a; 一、无人机运营合格证 无人机运营合格证是无人机运营企业或个人必须获得的证书&#xff0c;以确保无人机在运营过程中符合相关法规和标准。对于无人机运营合格证的具体要求和申请…

【React】React18 Hooks之useState

目录 useState案例1&#xff08;直接修改状态&#xff09;案例2&#xff08;函数式更新&#xff09;案例3&#xff08;受控表单绑定&#xff09;注意事项1&#xff1a;set函数不会改变正在运行的代码的状态注意事项2&#xff1a;set函数自动批量处理注意事项3&#xff1a;在下次…

【反悔堆 优先队列 临项交换 决策包容性】630. 课程表 III

本文涉及知识点 贪心 反悔堆 优先队列 临项交换 Leetcode630. 课程表 III 这里有 n 门不同的在线课程&#xff0c;按从 1 到 n 编号。给你一个数组 courses &#xff0c;其中 courses[i] [durationi, lastDayi] 表示第 i 门课将会 持续 上 durationi 天课&#xff0c;并且必…

E4.【C语言】练习:while和getchar的理解

#include <stdio.h> int main() {int ch 0;while ((ch getchar()) ! EOF){if (ch < 0 || ch>9)continue;putchar(ch);}return 0; } 理解上述代码 0-->48 9-->57 if行判断是否为数字&#xff0c;打印数字&#xff0c;不打印非数字

Selenium 切换 frame/iframe

环境&#xff1a; Python 3.8 selenium3.141.0 urllib31.26.19说明&#xff1a; driver.switch_to.frame() # 将当前定位的主体切换为frame/iframe表单的内嵌页面中 driver.switch_to.default_content() # 跳回最外层的页面# 判断元素是否在 frame/ifame 中 # 126 邮箱为例 # …

k8s公网集群安装(1.23.0)

网上搜到的公网搭建k8s都不太一致, 要么说的太复杂, 要么镜像无法下载, 所以写了一个简洁版,小白也能一次搭建成功 使用的都是centos7,k8s版本为1.23.0 使用二台机器搭建的, 三台也是一样的思路1.所有节点分别设置对应主机名 hostnamectl set-hostname master hostnamectl set…

javaSwing图书管理系统

一、 引言 图书管理系统是一个用于图书馆或书店管理图书信息、借阅记录和读者信息的应用程序。本系统使用Java Swing框架进行开发&#xff0c;提供直观的用户界面&#xff0c;方便图书馆管理员或书店工作人员对图书信息进行管理。以下是系统的设计、功能和实现的详细报告。 二…

Matplotlib Artist 1 概览

Matplotlib API中有三层 matplotlib.backend_bases.FigureCanvas&#xff1a;绘制区域matplotlib.backend_bases.Renderer&#xff1a;控制如何在FigureCanvas上绘制matplotlib.artist.Artist&#xff1a;控制render如何进行绘制 开发者95%的时间都是在使用Artist。Artist有两…

【C语言】自定义类型:联合和枚举

前言 前面我们学习了一种自定义类型&#xff0c;结构体&#xff0c;现在我们学习另外两种自定义类型&#xff0c;联合 和 枚举。 目录 一、联合体 1. 联合体类型的声明 2. 联合体的特点 3. 相同成员联合体和结构体对比 4. 联合体大小的计算 5. 用联合体判断当前机…

常见算法和Lambda

常见算法和Lambda 文章目录 常见算法和Lambda常见算法查找算法基本查找&#xff08;顺序查找&#xff09;二分查找/折半查找插值查找斐波那契查找分块查找扩展的分块查找&#xff08;无规律的数据&#xff09; 常见排序算法冒泡排序选择排序插入排序快速排序递归快速排序 Array…

hdu物联网硬件实验2 GPIO亮灯

学院 班级 学号 姓名 日期 成绩 实验题目 GPIO亮灯 实验目的 点亮三个灯闪烁频率为一秒 硬件原理 无 关键代码及注释 const int ledPin1 GREEN_LED; // the number of the LED pin const int ledPin2 YELLOW_LED; const int ledPin3 RED…