Java-多线程2

  1. 什么是线程?

线程是 cpu调度和执行的单位。
多个线程共享进程的方法区资源,但每个线程有自己的程序计数器虚拟机栈本地方法栈

 如何实现线程

继承Thread

实现步骤:
  1. 创建自定义类,继承Thread
  2.  重写run方法
  3.  创建自定义类对象,并.start()启动线程
class Th extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 2000; i++) {
            System.out.println(i + "Th");
        }
    }

    public static void main(String[] args) {
        Th th = new Th();
        th.start();
    }
}

实现Runnable接口

实现步骤 :
  1.  创建自定义类,实现Runnable接口
  2.  重写run()方法
  3.  创建Thread对象,把自定义类作为参数传进去
class Run implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 2000; i++) {
            System.out.println("Run"+i);
        }
    }
    public static void main(String[] args) {
        new Thread(new Run()).start();
    }
}

实现Callable接口

目的就是为了来处理 Runnable 不支持的用例。 Runnable 接口 不会返回结果或抛出检查异常,但是 Callable 接口 可以。所以,如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口 ,这样代 码看起来会更加简洁。
实现步骤 :
  1. 创建自定义类,实现Callable接口
  2. 重写call()方法
  3. 创建自定义类对象
  4. 创建执行服务(线程池)
  5. 提交服务
  6. 获取结果
  7. 关闭服务
class Cal implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        return null;
    }

    public static void main(String[] args) throws Exception {
        Cal cal = new Cal();
        Cal cal1 = new Cal();
        //创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //提交服务
        Future<Boolean> future = executorService.submit(cal);
        Future<Boolean> future1 = executorService.submit(cal1);
        //获取结果
        Boolean b = future.get();
        System.out.println(b);
        Boolean b1 = future1.get();
        System.out.println(b1);
        //关闭服务
        executorService.shutdown();
    }
}

线程的状态

线程创建之后它将处于 NEW (初始) 状态,调用 start() 方法后开始运行,线程这时候处于 READY (可运行) 状态。可运行状态的线程获得了 CPU 时间片( timeslice )后就处于 RUNNING (运 行) 状态。
当线程执行 wait() 方法之后,线程进入 WAITING (等待) 状态。进入等待状态的线程需要依靠其他 线程的通知才能够返回到运行状态,而 TIME_WAITING( 超时等待 ) 状态相当于在等待状态的基础上增加 了超时限制,比如通过 sleep long millis 方法或 wait long millis 方法可以将 Java 线程置 于 TIMED WAITING (超时等待) 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE (运行) 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED (阻塞) 状态。
线程在执行 Runnable run() 方法之后将会进入到 TERMINATED (终止) 状态。
线程执行 wait() 方法之后线程进入 Waiting 状态,由于调用 wait() 时会释放占用的 cpu 资源和锁,所以 当Waiting 线程被其他线程调用 Object.notify() 唤醒之后,需要重新获取对象上的锁。这时候也会先进入 Blocked状态等待获取锁。

sleep wait 有什么区别?

  1. wait 必须搭配 synchronize 一起使用,而 sleep 不需要;
  2. 进入 wait 状态的线程能够被 notify notifyAll 线程唤醒,而 sleep 状态的线程不能被 notify 方法 唤醒;
  3. wait 通常有条件地执行,线程会一直处于 wait 状态,直到某个条件变为真,但是 sleep 仅仅让线 程进入睡眠状态;
  4. wait 方法会释放对象锁,但 sleep 方法不会。
  5. 在调用 wait 方法之后,线程会变为 WATING(等待)状态,而调用 sleep 方法之后,线程会变为 TIMED_WAITING(超时等待)

ThreadLocal 

ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。可以通过set(T) 方法来设置一个值,在 当前线程下 再通过 get()方法获取到原先设置的值。ThreadLocal 线程安全
  1. Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本 (即T类型的变量)。
  2. 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以 ThreadLocal要保存的副本变量为value,存到threadLocals
  3. 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

ThreadLocal造成内存泄漏的原因?

ThreadLocalMap 中使用的 key ThreadLocal 的弱引用 , value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来, ThreadLocalMap中就会出现 key null Entry 。假如我们不做任何措施的话 ,value 永远无法被 GC 回 收,这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 set() 、 get()、 remove() 方法的时候,会清理掉 key null 的记录。使用完 ThreadLocal 方法后最好手动调用 remove()方法。

ThreadLocal内存泄漏解决方案?

每次使用完 ThreadLocal ,都调用它的 remove() 方法,清除数据。在使用线程池的情况下,没有及时清理ThreadLocal ,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就需要用完就清理。

线程同步

volatile

volatile Java 虚拟机提供的 轻量级 的同步机制。
volatile 关键字可以保证变量的可见性 ,如果我们将变量声明为 volatile ,这就指示 JVM ,这个变量是共 享且不稳定的,每次使用它都到主存中进行读取。
volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序 。 如果我们将变量声明为 volatile ,在对这个变量进行读写操作的时候,会通过插入特定的 内存屏障 的方式来禁止指令重排序。
volatile 关键字能保证变量的可见性,但不能保证对变量的操作是原子性的。

synchronized

老版本 Java 中的 synchronized 属于 重量级锁 ,效率低下。 因为监视器锁( monitor )是依赖于底层的 操作系统的 Mutex Lock 来实现的, Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者 唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核 态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。在 JDK6 之后, Java 官方从 JVM 层面对 synchronized 进行了较大优化。对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
synchronized 关键字解决的是多个线程之间访问资源的同步性, synchronized 关键字可以保证被它修饰 的方法或者代码块在任意时刻只能有一个线程执行。

synchronized控制对对象的访问,每个对象一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;方法一旦执行就独占该线程,直到该方法返回才释放锁,后面被阻 塞的线程才可以获得锁,继续执行。

synchronized的使用
修饰实例方法
当前对象实例 加锁,进入同步代码前要获得 当前对象实例的锁
public synchronized void buy() {
//同步实例方法
}
修饰静态方法
当前类 加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁 。这是因为静态成 员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。
public static synchronized void method(){
//同步静态方法
}
静态 synchronized 方法 非静态 synchronized 方法 之间的调用不互斥。因为访问 静态 synchronized
方法 占用的锁是 当前类 的锁,而访问 非静态 synchronized 方法 占用的锁是 当前实例对象 锁。
修饰代码块
synchronized(object) 表示进入同步代码块前要获得 给定对象的锁
synchronized(Object.class) 表示进入同步代码前要获得 给定 类的锁
void method(){
    synchronized (object/Object.class) {
        //同步代码块
    }
}
总结
synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁;
synchronized 关键字加到实例方法上是给对象实例上锁;

synchronized volatile 的区别

synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!
  1. volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比 synchronized 关键字要 好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
  2. volatile 关键字能保证数据的可见性,但不能保证数据的原子性。 synchronized 关键字两者都能保证。
  3.  volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

ReentrantLock

ReentrantLock 是一个可重入的互斥锁,又被称为 独占锁 。在同一个时间点只能被一个线程锁持有;可重入表示,ReentrantLock 可以被同一个线程多次获取。
ReentraantLock 是通过一个 FIFO 的等待队列来管理获取该锁所有线程的。在 公平锁 的机制下,线程依次排队获取锁;而“ 非公平锁 在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。
class Book implements Runnable {
    private ReentrantLock lock = new ReentrantLock();
    static private Integer n = 3;

    @Override
    public void run() {
        lock.lock();
        if (n <= 0) {
            lock.unlock();
            return;
        }
        n--;
        lock.unlock();
    }
}

synchronized ReentrantLock 的区别

两者都是可重入锁。 可重入锁 指的是自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果是不可重入锁的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增 1 ,所以要等到锁的计数器下降为 0 时才能释放锁。
  1. synchronized 依赖于 JVM ReentrantLock 依赖于 API 。synchronized 是依赖于 JVM 实现的, ReentrantLock JDK 层面实现的(也就是 API 层面,需要 lock() unlock() 方法配合try/finally 语句块来完成),所以可以通过查看它的源代码,来看它是如何实现的。
  2. ReentrantLock synchronized 增加了一些高级功能
    1. 等待可中断 : ReentrantLock 提供了通过 lock.lockInterruptibly() 来实现能够中断等待锁的线程的机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
    2. 可实现公平锁 : ReentrantLock 可以指定是公平锁还是非公平锁。而 synchronized 只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。 ReentrantLock 默认情况是非公平的,可以通过 ReentrantLock 类的 ReentrantLock(boolean fair) 构造方法来制定是否是 公平的。
    3. 可实现选择性通知(锁可以绑定多个条件): synchronized 关键字与 wait() 和 notify() / notifyAll() 方法相结合可以实现等待/通知机制。 ReentrantLock 类当然也可以实现,但是需要借助于 Condition 接口与 newCondition() 方法。

线程池

为什么使用线程池

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

创建线程

通过构造方法实现
public class Outer {
    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final Long KEEP_ALIVE_TIME = 1L;
    private static final int QUEUE_CAPACITY = 100;
    public static void main(String[] args) {
    //创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.AbortPolicy()
        );
        Runnable runnable = new Book();
        for (int i = 0; i < 10; i++) {
            executor.execute(runnable);
        }
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
    }
}
class Book implements Runnable {

    @Override
    public void run() {
        
    }
}
通过 Executor 框架的工具类 Executors 来实
创建三种类型的 ThreadPoolExecutor
  • newFixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。 ​​​​​​
public class Outer {
    public static void main(String[] args) {
//创建线程池
        ExecutorService executor=Executors.newFixedThreadPool(5);
        Runnable runnable = new Book();
        for (int i = 0; i < 10; i++) {
            executor.execute(runnable);
        }
        executor.shutdown();
        while (executor.isTerminated()){
        }
    }
}
  • newSingleThreadExecutor 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
public class Outer {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Runnable runnable = new Book();
        for (int i = 0; i < 10; i++) {
            executor.execute(runnable);
        }
        executor.shutdown();
        while (executor.isTerminated()) {
        }
    }
}
  • ​​​​​​newCachedThreadPool该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
public class Outer {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executor = Executors.newCachedThreadPool();
        Runnable runnable = new Book();
        for (int i = 0; i < 10; i++) {
            executor.execute(runnable);
        }
        executor.shutdown();
        while (executor.isTerminated()) {
        }
    }
}

Executor 框架

Java 5 之后,通过 Executor 来启动线程比使用 Thread start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。 this 逃逸是指在构造函 数返回之前其他线程就持有该对象的引用 . 调用尚未构造完全的对象的方法可能引发令人疑惑的错误 。 Executor 框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等, Executor 框架让并发编程变得更加简单。

Executor 框架结构(主要由三大部分组成)

  1. 任务。执行任务需要实现Runnable接口或Callable接口。
  2. 任务的执行。任务执行机制的核心接口Executor,以及继承自ExecutorExecutorService接口。 Executor框架ThreadPoolExecutorScheduledThreadPoolExecutor这两个关键类实现了ExecutorService接口。实际上我们需要更多关注的是 ThreadPoolExecutor 这个类,这个类在我们实际使用线程池的过程中,使用频率还是非常高的。
  3. 异步计算的结果。包括接口Future和实现Future接口的FutureTask类。

Executor 框架的使用

  1. 主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
  2. 把创建完成的实现 Runnable / Callable 接口的 对象直接交给 ExecutorService 执行: ExecutorService.execute(Runnable command)或者也可以把 Runnable 对象或 Callable 对象提交给 ExecutorService 执行( ExecutorService.submitRunnable task) ExecutorService.submitCallable <T> task)。
  3. 如果执行 ExecutorService.submit(…) ExecutorService 将返回一个实现 Future 接口的对象(我们刚刚也提到过了执行 execute() 方法和 submit() 方法的区别, submit() 会返回一个 FutureTask 对象)。由于 FutureTask 实现了 Runnable ,我们也可以创建FutureTask ,然后直接交给 ExecutorService 执行。
  4. 最后,主线程可以执行 FutureTask.get() 方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning) 来取消此任务的执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习面试笔试之循环神经网络(RNN)、门控循环单元(GRU)、长短期记忆(LSTM)

深度学习面试笔试之循环神经网络RNN、门控循环单元GRU、长短期记忆LSTM 循环神经网络(RNN)1. 什么是RNN1.1 RNN的应用1.2 为什么有了CNN&#xff0c;还要RNN?1.3 RNN的网络结构1.4 双向RNN1.5 BPTT算法 2. 其它类型的RNN3. CNN与RNN的区别4. 为什么RNN 训练的时候Loss波动很大…

aws(学习笔记第七课) 私有子网使用NAT服务器

aws(学习笔记第七课) AWS的私有子网使用NAT服务器 学习内容&#xff1a; AWS的私有子网使用NAT服务器 1. AWS的私有子网使用NAT服务器 在上面的例子的网络构成图中&#xff0c;可能会发现一个问题。就是Private Subnet的Apache server无法访问互联网。比如&#xff0c;当需要…

MySQL【知识改变命运】10

联合查询 0.前言1.联合查询在MySQL里面的原理2.练习一个完整的联合查询2.1.构造练习案例数据2.2 案例&#xff1a;⼀个完整的联合查询的过程2.2.1. 确定参与查询的表&#xff0c;学⽣表和班级表2.2.2. 确定连接条件&#xff0c;student表中的class_id与class表中id列的值相等2.…

Win11右键默认显示更多选项

Win11默认显示 想要效果 解决方案1 先按住Shift键&#xff0c;再按右键试试。 解决方案2 1.启动命令行&#xff0c;输入命令 reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve2.显示操作成功完成&#…

2024java高频面试之JVM-第二弹

什么是 STW Java 中「「Stop-The-World机制简称 STW」」 &#xff0c;是在执行垃圾收集算法时&#xff0c;Java 应用程序的其他所有线程都被挂起&#xff08;除了垃圾收集帮助器之外&#xff09;。「Java 中一种全局暂停现象&#xff0c;全局停顿」&#xff0c;所有 Java 代码…

子比美化 – WP添加网站翻译功能 | 实现国际化多语言[js翻译]

前言 本教程适用于子比主题&#xff0c;其它程序或主题请自行适配&#xff01;&#xff01;&#xff01; 图片展示 目前支持五种语言 教程开始 首先在后台自定义CSS代码中添加以下代码 .ignore:hover{color:var(--theme-color);transition:color .2s,transform .3s;}#tran…

怎么通过docker搭建一个mqtt服务器

由于debug需要排查mqtt的连接问题&#xff0c;为了方便&#xff0c;自己在云服务器上搭建一个mqtt服务器。 文中涉及的IP是虚构的IP&#xff0c;请替换成自己云服务器的IP&#xff0c;如有雷同&#xff0c;纯属巧合。 大致分为三部分&#xff1a; 一、安装docker 二、安装m…

cisco网络安全技术第3章测试及考试

测试 使用本地数据库保护设备访问&#xff08;通过使用 AAA 中央服务器来解决&#xff09;有什么缺点&#xff1f; 试题 1选择一项&#xff1a; 必须在每个设备上本地配置用户帐户&#xff0c;是一种不可扩展的身份验证解决方案。 请参见图示。AAA 状态消息的哪一部分可帮助…

<Project-11 Calculator> 计算器 0.2 工时计算器 WorkHours Calculator HTTP + JS

灵感 给工人发工资是按小时计算的&#xff0c;每次都要上网&#xff0c;我比较喜欢用 Hours Calculator &#xff0c;也喜欢它的其它的功能&#xff0c; 做个类似的。 我以为是 Python&#xff0c;结果在学 javascript 看 HTML&#xff0c;页面的基础还停留在 Frontpage 2000…

Cloudlog delete_oqrs_line 未授权SQL注入漏洞复现

0x01 产品简介 Cloudlog 是一个自托管的 PHP 应用程序,可让您在任何地方记录您的业余无线电联系人。使用PHP和MySQL构建的基于Web的业余无线电记录应用程序支持从HF到微波的一般站记录任务 0x02 漏洞概述 Cloudlog delete_oqrs_line 接口存在未授权SQL注入漏洞,未经身份验…

Marin说PCB之GMSL2 的Layout走线的注意事项

昨天有一位铁粉私信问我能不能讲解一下GMSL走线的一些注意事项啥的&#xff0c;我说当等我从以色列出差回来就给你更新一下这个&#xff0c;当然后来又很多的热心的粉丝提出很多的想法&#xff0c;我会一一给大家解答分享的&#xff0c;本期文章主要先给大家分享一下美信的手册…

[Python学习日记-50] Python 中的序列化模块 —— pickle 和 json

[Python学习日记-50] Python 中的序列化模块 —— pickle 和 json 简介 pickle 模块 json 模块 pickle VS json 简介 什么叫序列化&#xff1f; 序列化指的是将对象转换为可以在网络上传输或者存储到文件系统中的字节流的过程。序列化使得对象可以被保存、传输和恢复&#…

机器学习与神经网络:科技的星辰大海

前提 近日&#xff0c;2024年诺贝尔物理学奖颁发给了机器学习与神经网络领域的研究者&#xff0c;这是历史上首次出现这样的情况。这项奖项原本只授予对自然现象和物质的物理学研究作出重大贡献的科学家&#xff0c;如今却将全球范围内对机器学习和神经网络的研究和开发作为了一…

基于K8S的StatefulSet部署mysql主从

StatefulSet特性 StatefulSet的网络状态 拓扑状态&#xff1a;应用的多个实例必须按照某种顺序启动&#xff0c;并且必须成组存在&#xff0c;例如一个应用中必须存在一 个A Pod和两个B Pod&#xff0c;且A Pod必须先于B Pod启动的场景 存储状态&#xff1a;应用存在多个实例…

ChatGPT01-preivew体验报告:内置思维链和多个llm组合出的COT有啥区别呢?丹田与练气+中学生物理奥赛题测试,名不虚传还是名副其实?

一个月前&#xff0c;o1发布的时候&#xff0c;我写了篇文章介绍 逻辑推理能力堪比博士生&#xff0c;OpenAI发布全新AI模型系列&#xff1a; o1 - 大模型或许进入新阶段&#xff0c;还翻译了官方的介绍 解密OpenAI o1是如何让LLMs获得逻辑推理能力的 - CoT * RL&#xff0c;也…

【Linux】多线程安全之道:互斥、加锁技术与底层原理

目录 1.线程的互斥 1.1.进程线程间的互斥相关背景概念 1.2.互斥量mutex的基本概念 所以多线程之间为什么要有互斥&#xff1f; 为什么抢票会抢到负数&#xff0c;无法获得正确结果&#xff1f; 为什么--操作不是原子性的呢&#xff1f; 解决方式&#xff1a; 2.三种加锁…

基于SpringBoot+Vue的厨艺交流系统的设计与实现(源码+定制开发)厨艺知识与美食交流系统开发、在线厨艺分享与交流平台开发、智能厨艺交流与分享系统开发

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

第五届人工智能与教育国际学术会议(ICAIE 2024)

文章目录 一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题六、咨询 一、会议详情 二、重要信息 大会官网&#xff1a;https://ais.cn/u/vEbMBz提交检索&#xff1a;EI Compendex、IEEE Xplore、Scopus 三、大会介绍 第五届人工智能与教育国际学术会议&#x…

java逻辑运算符 C语言结构体定义

1. public static void main(String[] args) {System.out.println(true&true);//&两者均为true才trueSystem.out.println(false|false);// | 两边都是false才是falseSystem.out.println(true^false);//^ 相同为false&#xff0c;不同为trueSystem.out.println(!false)…

【python爬虫实战】爬取全年天气数据并做数据可视化分析!附源码

由于篇幅限制&#xff0c;无法展示完整代码&#xff0c;需要的朋友可在下方获取&#xff01;100%免费。 一、主题式网络爬虫设计方案 1. 主题式网络爬虫名称&#xff1a;天气预报爬取数据与可视化数据 2. 主题式网络爬虫爬取的内容与数据特征分析&#xff1a; - 爬取内容&am…