编程必备:JAVA多线程详解

news2024/11/28 20:32:46

目录

前言

1.入门多线程

1.1. 线程、进程、多线程、线程池

1.2.并发、串行、并行

1.3. 线程的实现方式

1.3.1. 继承 Thread 类

1.3.2. 实现 Runnable 接口

1.3.3. 使用 Callable 和 Future

1.3.4. 使用线程池

1.4.线程的状态

1.5. 线程常用方法

1.5.1 sleep()

1.4.2 join()

1.5.3 yield()

1.5.4.wait() 和 notify() 

1.5.5.-interrupt()方法和stop()方法 

1.5.6. setPriority (int newPriority)、getPriority()

1.6.线程调度

2.进阶多线程

2.1.多线程引发问题

2.2.多线程解决方法

2.2.1. 锁机制

2.2.3. 线程池

2.2.4. 并发工具类

3.面试题

3.1.sleep()和wait()有什么区别?

3.2.如何停止一个处在运行态的线程

3.3.interrupt()、interrupted()、isInterrupted()方法的区别

3.4.系统创建的线程过多会有什么影响

4.写在最后

鸣谢


前言

多线程已经成为一种常见的编程模式,广泛应用于各种不同类型的应用程序中。

本篇博客文章中,我们将会探讨多线程编程的相关知识和技巧。通过代码示例和实际应用案例来深入了解多线程的具体实现和应用方法,帮助更好地掌握多线程编程技术,提高程序效率和性能。后期随学习深入还会补充修改。

1.入门多线程

1.1. 线程、进程、多线程、线程池

线程

在计算机科学中,线程是指进程中的一个单独的执行路径。一个进程可以包含多个线程,每个线程都可以并行执行不同的任务。多线程编程是指在同一时间内运行多个线程来完成多个任务。

多线程

多线程是指在同一时间内运行多个线程来完成多个任务。多线程提高程序的性能和响应速度。但是增加了代码的复杂性,同时需要考虑线程安全和死锁等问题。

线程池

线程池是一组预先创建的线程,它们可以被重复使用来执行多个任务。使用线程池可以避免在创建和销毁线程时产生额外的开销,从而提高程序的性能。Java 中提供了 Executor 框架来实现线程池。

进程

进程即一段程序的执行过程,是计算机中的程序关于某数据集合上的一次运行活动,是系统分配资源的最小单位

线程与进程的区别

  • 根本区别:进程是操作系统资源分配的最小单位,而线程是处理任务调度和执行的最小单位
  • 资源开销:每个进程都有单独的代码和数据空间,进程之间的切换会有较大的开销;线程可以开做轻量级的进程,同一类线程共享代码和数据空间,每个线程有自己独立的运行栈和程序计数器,线程之间的切换开销较小
  • 包含关系:一个进程可以包含多个线程,这些线程可以共享同步资源
  • 内存分配:同一个进程中的所有线程共享本进程的地址空间和资源,而进程之间的地址空间和资源相互独立
  • 影响关系:一个进程崩溃后,不会影响其他进程;而一个线程崩溃后其他线程也会收到影响,从而整个进程崩溃
  • 执行过程:每个独立的进程都有程序运行的入口,顺序执行序列和出口,但线程不能独立执行,必须依存于进程

1.2.并发、串行、并行

并发、串行、并行概念

  • 并发:指多个任务在同一时间段同一个CPU上交替执行,看起来好像是同时执行的。例如,多个线程在同一时间内运行。
  • 串行:指多个任务按照顺序依次执行,一个任务完成后才能执行下一个任务。例如,单线程程序就是串行执行的。
  • 并行:多个处理器或多核处理器同时处理多个任务,必须需要有多个处理器或者多核 CPU 才能实现,否则只能是并发。例如,多个线程在不同的处理器或者 CPU 上运行。

并发、串行、并行的区别

  • 执行方式:并发和串行都在单个处理器上执行,但是并发是多个任务交替执行,串行是按照顺序依次执行;并行需要多个处理器或多核 CPU 才能实现。
  • 性能:并发和并行都可以提高程序的性能和响应速度,但是并发需要考虑线程安全和死锁等问题;串行虽然简单稳定,但是无法充分利用多核 CPU 的优势。
  • 实现方式:并发可以使用多线程技术实现;串行只能使用单线程实现;并行需要多个处理器或多核 CPU 才能实现。

并发编程的三要素

  • 原子性:指一个或多个操作要么全部执行成功,要么全部执行失败
  • 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行(处理器可能会对指令进行重排序)

多线程和并发

多线程是指在同一时间内运行多个线程来完成多个任务。多线程可以提高程序的性能和响应速度,因为它们可以同时执行多个任务。

并发是指在同一时间内执行多个任务的能力。并发可以通过使用多线程来实现,但也可以通过其他方式实现,例如使用异步编程或事件驱动编程。

因此,多线程是实现并发的一种方式,但并发不一定需要使用多线程。另外,多线程编程中需要考虑的问题,例如线程安全和死锁等,在并发编程中同样需要考虑

1.3. 线程的实现方式

        多线程是提高程序性能和响应速度的重要手段,Java 中有多种实现方式, 

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 使用 Callable 和 Future
  • 使用线程池

1.3.1. 继承 Thread 类

  • 介绍:通过继承 Thread 类来实现多线程。
  • 示例代码:展示如何继承 Thread 类并重写 run() 方法。
  • 优点:实现简单,易于理解。
  • 缺点:无法继承其他类,因为 Java 不支持多重继承。
class MyThread extends Thread {
    public void run() {
        // 执行需要的代码
    }
}

MyThread thread = new MyThread();
thread.start();

1.3.2. 实现 Runnable 接口

  • 介绍:通过实现 Runnable 接口来实现多线程。
  • 示例代码:展示如何实现 Runnable 接口并重写 run() 方法。
  • 优点:可以继承其他类,因为 Java 支持实现多个接口。
  • 缺点:需要创建 Thread 对象来启动线程。
  • 步骤:
     - 自定义线程类实现Runnable接口
     - 实现run()方法,编写线程体
     - 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
class MyRunnable implements Runnable {
    public void run() {
        // 执行需要的代码
    }
}

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();

1.3.3. 使用 Callable 和 Future

  • 介绍:通过使用 Callable 和 Future 接口来实现多线程。
  • 示例代码:展示如何使用 Callable 和 Future 接口来创建和启动线程。
  • 优点:可以获取线程执行的返回值。
  • 缺点:相比于前两种方式,实现稍微复杂一些。
  •  步骤:
     - 实现Callable接口,先要返回值类型
     - 重写call()方法,需要抛出异常
     - 创建目标对象
     - 创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
     - 提交执行:Future<Boolean> res = ser.submit(t1);
     - 获取结果:boolean r1 = res.get();
     - 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;

// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        // 线程执行体
        for (int i = 0; i < 10; i++) {
            System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
        }

        return true;
    }

    public static void main(String[] args) throws ExecutionException,
        InterruptedException {
        // main线程,主线程

        // 创建线程实现类对象
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();

        // 创建执行服务,参数是线程池线程数量
        ExecutorService ser = Executors.newFixedThreadPool(2);
        // 提交执行
        Future<Boolean> res = ser.submit(thread);
        Future<Boolean> res2 = ser.submit(thread2);
        // 获取结果
        boolean r1 = res.get();
        boolean r2 = res2.get();
        // 关闭服务
        ser.shutdownNow();
    }
}

1.3.4. 使用线程池

  • 介绍:通过使用线程池来实现多线程。
  • 示例代码:展示如何使用 Executor 和 ExecutorService 接口来创建和管理线程池。
  • 优点:可以重用线程,避免了频繁创建和销毁线程的开销。
  • 缺点:需要对线程池的大小进行合理配置,否则可能会导致性能问题。
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() {
    public void run() {
        // 执行需要的代码
    }
});

1.4.线程的状态

  • 新建(New):当线程对象创建后,线程处于新建状态。
  • 运行(Runnable):当调用线程的 start() 方法后,线程处于就绪状态,等待 CPU 调度执行。
  • 阻塞(Blocked):当线程等待某个条件(如 I/O 操作、锁)时,线程处于阻塞状态。
  • 等待(Waiting):当线程等待某个条件的唤醒(如调用 wait() 方法)时,线程处于等待状态。
  • 超时等待(Timed Waiting):当线程等待某个条件的唤醒,但是等待一定的时间后会自动唤醒(如调用 sleep() 方法或者带超时参数的 wait() 方法)时,线程处于超时等待状态。
  • 终止(Terminated):当线程执行完成或者出现异常时,线程处于终止状态。

1.5. 线程常用方法

  • start():启动线程。
  • run():线程执行的代码。
  • join():等待线程执行完毕。
  • sleep():使线程休眠一段时间。
  • interrupt():中断线程的执行。
  • wait() 使当前线程等待另一个线程发出通知。
  • notify() 通知等待的线程继续执行。
  • setPriority (int newPriority)、getPriority() 改变、获取线程的优先级。

1.5.1 sleep()

sleep() 方法可以使当前线程暂停指定的时间。例如:

try {
    Thread.sleep(1000); // 暂停 1 秒钟
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.4.2 join()

join() 方法可以等待指定的线程执行完毕。例如:

Thread thread1 = new Thread(() -> {
    // 执行需要的代码
});

Thread thread2 = new Thread(() -> {
    // 执行需要的代码
});

thread1.start();
thread2.start();

try {
    thread1.join(); // 等待 thread1 执行完毕
    thread2.join(); // 等待 thread2 执行完毕
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.5.3 yield()

暂停当前正在执行的线程对象,并执行其他线程,yield() 方法可以让出 CPU 时间片,使其他线程有机会运行。

例如:

Thread.yield();

yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。

1.5.4.wait() 和 notify() 

在多线程程序中,可以使用 wait()notify() 方法来协调多个线程之间的操作。wait() 方法可以使当前线程等待另一个线程发出通知,而 notify() 方法可以通知等待的线程继续执行。

public class Message {
    private String content;
    private boolean available = false;

    public synchronized String getContent() {
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        available = false;
        notifyAll();
        return content;
    }

    public synchronized void setContent(String content) {
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.content = content;
        available = true;
        notifyAll();
    }
}

public class Main {
    public static void main(String[] args) {
        Message message = new Message();
        // 创建两个线程来发送和接收消息
        new Thread(() -> {
            message.setContent("Hello");
        }).start();
        new Thread(() -> {
            System.out.println(message.getContent()); // 输出 Hello
        }).start();
    }
}

1.5.5.-interrupt()方法和stop()方法 

- JDK提供的stop方法已废弃,不推荐使用。

- 推荐线程自动停止下来,建议使用一个标识位变量进行终止,当flag=false时,则终止线程运行。

public class DemoInterrupt {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 终断t线程的睡眠(这种终断睡眠的方式依靠了java的异常处理机制。)
        t.interrupt();
        //        t.stop(); //强行终止线程
        //缺点:容易损坏数据  线程没有保存的数据容易丢失
    }
}
class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            // 睡眠1天
            Thread.sleep(1000 * 60 * 60 * 24);
        } catch (InterruptedException e) {
        //            e.printStackTrace();
        }
        //1天之后才会执行这里
        System.out.println(Thread.currentThread().getName() + "---> end");
 
    }
}

1.5.6. setPriority (int newPriority)、getPriority()

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。 - 线程的优先级用数据表示,范围1~10。 - 线程的优先级高只是表示他的权重大,获取CPU执行权的几率大。 - 先设置线程的优先级,在执行start()方法

public class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程优先级:"
            + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread,"a");
        Thread thread2 = new Thread(myThread,"b");
        Thread thread3= new Thread(myThread,"c");
        Thread thread4= new Thread(myThread,"d");
        thread3.setPriority(Thread.MAX_PRIORITY);
        thread.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.NORM_PRIORITY);
        thread4.setPriority(8);
        thread.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

结果如下:

c线程优先级:10
b线程优先级:5
a线程优先级:1
d线程优先级:8

1.6.线程调度

  • 线程调度模型

    • 均分式调度模型:所有的线程轮流使用CPU的使用权,平均分配给每一个线程占用CPU的时间。

    • 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个线程来执行,优先级高的占用CPU时间相对来说会高一点点。

    Java中JVM使用的就是抢占式调度模型

  • getPriority():获取线程优先级

  • setPriority:设置线程优先级

2.进阶多线程

2.1.多线程引发问题

多线程编程能够提高程序的性能和响应能力,但同时也会带来一些问题。主要包括以下几个方面:

  1.竞态条件(Race Condition):当多个线程同时访问共享资源时,由于线程执行顺序的不确定性,可能会导致程序的输出结果出现错误。例如,多个线程同时对一个计数器进行自增操作,如果没有进行同步,可能会导致计数器的值不正确。

        可以使用 synchronized 关键字来解决竞态条件问题。

  2.死锁(Deadlock):当多个线程相互等待对方释放所占用的资源时,可能会陷入死锁状态,无法继续执行。例如,线程 A 占用了资源 1,等待资源 2,而线程 B 占用了资源 2,等待资源 1,两个线程都无法继续执行。

        可以使用锁和条件变量来解决死锁问题。

  3.饥饿(Starvation):当某些线程由于竞争共享资源失败而无法继续执行时,可能会出现饥饿问题。例如,如果一个线程在一个高负载的系统中请求资源,它可能会等待很长时间才能获得所需的资源。

       4.上下文切换:当多个线程同时运行时,操作系统需要进行上下文切换,这会消耗一定的系统资源。如果线程数量过多,上下文切换的开销可能会超过程序本身的开销

        5.线程安全:在多线程程序中,需要确保共享资源的安全性。可以使用锁和原子变量来实现线程安全。

        6.性能优化:在多线程程序中,需要考虑性能优化问题。可以使用线程池和并发集合来提高程序性能。

2.2.多线程解决方法

2.2.1. 锁机制

  • 定义:锁机制是解决多线程之间互斥访问共享资源的一种方式。
  • 实现方式:使用 synchronized 关键字、ReentrantLock 类或 ReadWriteLock 接口等实现锁机制。
  • 常用工具:Lock、Condition、Semaphore、ReadWriteLock 等。

2.2.3. 线程池

  • 定义:线程池是管理和调度多个线程的一种机制,可以避免频繁创建和销毁线程带来的性能开销。
  • 实现方式:使用 Executors 类或 ThreadPoolExecutor 类创建和管理线程池。
  • 常用参数:核心线程数、最大线程数、任务队列、拒绝策略等。

2.2.4. 并发工具类

  • 定义:并发工具类是解决并发编程中常见问题的一种工具,例如阻塞队列、计数器、信号量等。
  • 实现方式:使用 Java.util.concurrent 包中提供的工具类实现并发编程。
  • 常用工具:ArrayBlockingQueue、CyclicBarrier、Semaphore、CountDownLatch 等。

3.面试题

3.1.sleep()和wait()有什么区别?

两者都可暂停当前线程

  • 所在类不同:sleep()时Thread类的静态方法,wait()是Object类的方法
  • 释放锁:sleep()不会释放锁,wait()会释放锁
  • 用途不同:wait()通常用于线程间通信,sleep()通常用于暂停线程
  • 结果不同:sleep()方法执行完成后,线程会再次进入就绪态;wait()方法被notify()唤醒后,线程会进入同步队列重新抢占锁

3.2.如何停止一个处在运行态的线程

  1. 该线程的run()方法执行结束后线程自动终止
  2. 使用stop()方法强制终止,但一般很少这么用
  3. 使用interrupt()方法中断线程(其流程为,设置一个中断标志位,调用interrupt()方法通知系统请求关闭线程,待系统在适合的时间自行中断)

3.3.interrupt()、interrupted()、isInterrupted()方法的区别

  • interrupt()方法和isInterrupted()方法都是实例方法,通过Thread对象调用;interrupted()则是静态方法,通过Thread类调用,三个方法都是与线程中断有关的
  • interrupt()方法:用来设置中断标志位,通知系统请求结束线程,由系统决定具体何时中断

此时如果线程在阻塞状态:
那么就会抛出InterruptedException异常,并重置中断标志位

如果线程不在阻塞状态:
使用Thread.interrupted()判断当前中断标志位是否被设置,并重置中断标志位
使用Thread.currentThread.isInterrupted()判断当前中断标志位是否被设置,不重置中断标志位

3.4.系统创建的线程过多会有什么影响

  • 线程的生命周期开销非常高
  • 消耗过多的CPU
  • 降低JVM的效率

4.写在最后

以上就是我对多线程的个人简介,后续会不断完善更新,与大家共勉

鸣谢

[1] https://blog.csdn.net/zdl66/article/details/126297036

[2] https://blog.csdn.net/m0_46233999/article/details/118702235

[3] https://blog.csdn.net/qq_29141913/article/details/125964815#3_16

[4] https://blog.csdn.net/YQQAGH178/article/details/119828128

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

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

相关文章

验证码识别系统Python,基于CNN卷积神经网络算法

一、介绍 验证码识别系统&#xff0c;使用Python作为主要开发语言&#xff0c;基于深度学习TensorFlow框架&#xff0c;搭建卷积神经网络算法。并通过对数据集进行训练&#xff0c;最后得到一个识别精度较高的模型。并基于Django框架&#xff0c;开发网页端操作平台&#xff0…

基于Java网上花店系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

Prompt 范式产业实践分享!基于飞桨 UIE-X 和 Intel OpenVINO 实现跨模态文档信息抽取

近期 Prompt 范式备受关注&#xff0c;实际上&#xff0c;其思想在产业界已经有了一些成功的应用案例。中科院软件所和百度共同提出了大一统诸多任务的通用信息抽取技术 UIE&#xff08;Universal Information Extraction&#xff09;。截至目前&#xff0c;UIE 系列模型已发布…

【JavaEE】网络原理——传输层协议:UDP和TCP

目录 1、简单了解应用层协议 2、传输层UDP协议 3、传输层TCP协议 3.1、TCP报文介绍 3.2、TCP实现可靠传输的核心机制 3.2.1、确认应答 3.2.2、超时重传 3.3、连接管理 &#xff08;三次挥手&#xff0c;四次握手&#xff09; 3.3.1、建立连接&#xff08;三次握手&a…

Java-API简析_java.lang.Enum<E extends Enum<E>>类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/131212897 出自【进步*于辰的博客】 其实我的【Java-API】专栏内的博文对大家来说意义是不大的。…

接口自动化测试丨如何实现多套环境的自动化测试?

在敏捷迭代的项目中&#xff0c;通常会将后台服务部署到多套测试环境。那么在进行接口自动化测试时&#xff0c;则需要将服务器的域名进行配置。使用一套接口测试脚本&#xff0c;通过切换域名地址配置&#xff0c;实现多套环境的自动化测试。 实战练习 分别准备两套测试环境…

Redis的单线程模型和标准Reactor线程模型的关系

文章目录 Redis到底是不是单线程&#xff1f;标准Reactor线程模型单reactor单线程单reactor多线程多reactor多线程 redis6.0 之前的单线程模型redis6.0 之后的单线程模型为什么redis最初选择的单线程网络模型&#xff1f;为什么redis6.0 io读写要用多线程&#xff1f; Redis 6.…

索尼RSV视频修复方法论视频文件修复时样本文件的三同

索尼RSV类的文件修复案例有很多&#xff0c;程序操作也很简单没什么可说的&#xff0c;这次这个索尼ILCE-7SM3的案例就是为了让大家更好的认识视频修复中我称之为“三同“的重要性&#xff0c;想要恢复的效果好必须要把准备工作做到位。 故障文件:45.1G RSV文件 故障现象: 索…

软件渗透测试是什么?软件产品哪种情况下需要做渗透测试?

随着互联网的普及&#xff0c;软件的开发方越来越多&#xff0c;但是随之而来的也是信息安全方面的问题。在软件开发过程中&#xff0c;安全问题一定要被重视&#xff0c;因为漏洞和安全问题一旦被黑客利用&#xff0c;会给公司和用户带来巨大的损失。为了避免这种情况的发生&a…

语音工牌在运营商智慧装维场景,有何应用价值?

客户精细化运营时代&#xff0c;如何做好客户服务体验&#xff0c;提升品牌美誉度和好感度&#xff0c;是众多企业开始思考的问题。 在运营商行业&#xff0c;上门装维和营业厅服务场景是企业与客户直接互动最多的地方。这个过程的服务质量直接影响到客户成交率、客户投诉率和…

软件测试金融银行项目如何测?从业务到测试实战,超细总结整理...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 金融行业的业务特…

chatgpt赋能python:Python怎么打备注?让你的代码更加清晰易懂

Python怎么打备注&#xff1f;让你的代码更加清晰易懂 Python是一种流行的编程语言&#xff0c;可以用来构建不同类型的应用程序&#xff0c;从网站到数据分析。无论您是初学者还是经验丰富的开发人员&#xff0c;写清晰&#xff0c;易于理解的代码都是非常重要的&#xff0c;…

Linux之ACL权限

目录 Linux之ACL权限 场景 设定ACL权限 ACL权限管理命令 参数及作用 给用户和用户组添加ACL权限 案例 创建 目录 /project 的所有者和所属组其他人权限设定为 770 创建旁听用户pt,并赋予ACL权限rx 查看目录/project的ACL权限 验证pt 用户对于 /project 目录没有写权…

el-element-admin实现双路由菜单

需求&#xff1a; 1、输入用户名登录企业级菜单 2、点击企业级菜单中的首页&#xff0c;右边显示项目列表&#xff0c;点击某一行跳转到项目级菜单 注意&#xff1a; 企业级菜单和项目级菜单&#xff0c;后端分别给接口 具体实施&#xff1a; 1、点击面包靴首页的时候设置标记…

第14届蓝桥杯国赛真题剖析-2023年5月28日Scratch编程初中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第149讲。 第14届蓝桥杯Scratch国赛真题&#xff0c;这是2023年5月28日上午举办的全国总决赛&#xff0c;比赛仍然采取…

基于java springboot的图书管理系统设计和实现

基于java springboot的图书管理系统设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录…

load_dataset加载huggingface数据集失败

1. 一般的加载方式 from datasets import load_dataset dataset_dict load_dataset(cmrc2018)这种加载方式可能会显示因为连接问题导致失败&#xff0c;此时可以在hugging face里面找到对应的页面下载下来 然后改一下代码&#xff1a; from datasets import load_dataset d…

关于TFTP传输协议

TFTP&#xff08;Trivial File Transfer Protocol,简单文件传输协议&#xff09;&#xff1a;实现客户端与服务器之间简单文件传输。小文件传输&#xff0c;端口&#xff1a;69。协议简单&#xff0c;易于实现。 缺点&#xff1a; 传输效率低对于超时机制没有明确说明每包长度…

华为OD机试真题B卷 JavaScript 实现【数组拼接】,附详细解题思路

一、题目描述 现在有多组整数数组&#xff0c;需要将它们合并成一个新的数组。 合并规则&#xff0c;从每个数组里按顺序取出固定长度的内容合并到新的数组中&#xff0c;取完的内容会删除掉&#xff0c;如果该行不足固定长度或者已经为空&#xff0c;则直接取出剩余部分的内…

第18章:逻辑架构

一、逻辑架构剖析 1.1服务器处理客户端请求 ①MySQL是典形的C/S架构&#xff0c;服务器端使用mysqld ②客户端进程向服务器进程发送SQL语句&#xff0c;服务器进程在向客户端进程发送处理结果。 查询请求演示图 查询请求详细图 1.2第1层&#xff1a;连接层 ①客户端访问My…