读书笔记-Java并发编程的艺术-第4章(Java并发编程基础)-第1节(线程简介)

文章目录

  • 4.1 线程简介
    • 4.1.1 什么是线程
    • 4.1.2 为什么要使用多线程
    • 4.1.3 线程优先级
    • 4.1.4 线程的状态
    • 4.1.5 Daemon 线程

Java从诞生开始就明智地选择了内置对多线程的支持,这使得Java语言相比同一时期的其他语言具有明显的优势。线程作为操作系统调度的最小单元,多个线程能够同时执行这将显著提升程序性能,在多核环境中表现得更加明显。但是,过多地创建线程和对线程的不当管理也容易造成问题。本章将着重介绍Java并发编程的基础知识,从启动一个线程到线程间不同的通信方式,最后通过简单的线程池示例以及应用(简单的Web服务器)来串联本章所介绍的内容。

4.1 线程简介

4.1.1 什么是线程

现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。

一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序,因为执行main0方法的是一个名称为main的线程。可以使用JMX来查看一个普通的Java程序包含哪些线程。示例代码如下:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class MultiThread {
    public static void main(String[] args) {
        // 获取Java线程管理MXBean
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

        // 不需要获取同步的monitor和synchronizer信息,仅获取线程的线程堆栈信息
        ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);

        // 通历线程信息,仅打印线程ID和线程名称信息
        for (ThreadInfo threadInfo : threadInfos) {
            System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());
        }
    }
}

Java Management Extensions(JMX)是一种Java标准,用于管理和监控Java应用程序,特别是分布式系统。它提供了一种标准化的方式来管理应用程序的各种方面,包括性能监控、配置更改、事件通知等。目前JMX最常用的就是用来做JAVA程序的监控,市面上常见的Java 监控框架基本都是基于JMX来实现的。

输出如下所示(输出内容可能不同)。

[6] Monitor Ctrl-Break
[5] Attach Listener
[4] Signal Dispatcher // 分发处理发送给JVM信号的线程
[3] Finalizer // 调用对象finalize方法的线程
[2] Reference Handler //清除Reference的线程
[1] main // main 线程,用户程序入口

可以看到,一个Java程序的运行不仅仅是main()方法的运行,而是main 线程和多个其他线程的同时运行。

4.1.2 为什么要使用多线程

执行一个简单的“Hello,World!”,却启动了那么多的“无关”线程,是不是把简单的问题复杂化了?当然不是,因为正确使用多线程,总是能够给开发人员带来显著的好处,而用多线程的原因主要有以下几点。

  • 1. 更多的处理器核心

随着处理器上的核心数量越来越多,以及超线程技术的广泛运用,现在大多数计算机都比以往更加擅长并行计算,而处理器性能的提升方式,也从更高的主频向更多的核心发展。如何利用好处理器上的多个核心也成了现在的主要问题。

线程是大多数操作系统调度的基本单元,一个程序作为一个进程来运行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。试想一下,一个单线程程序在运行时只能使用一个处理器核心,那么再多的处理器核心加入也无法显著升该程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。

  • 2. 更快的响应时间

有时我们会编写一些较为复杂的代码(这里的复杂不是说复杂的算法,而是复杂的业务逻辑),例如,一笔订单的创建,它包括插入订单数据、生成订单快照、发送邮件通知卖家和记录货品销售数量等。用户从单击“订购”按钮开始,就要等待这些操作全部完成才能看到订购成功的结果。但是这么多业务操作,如何能够让其更快地完成呢?

在上面的场景中,可以使用多线程技术,即将数据一致性不强的操作派发给其他线程处理(也可以使用消息队列),如生成订单快照、发送邮件等。这样做的好处是响应用户请求的线程能够尽可能快地处理完成,缩短了响应时间,提升了用户体验。

  • 3. 更好的编程模型

Java为多线程编程提供了良好、考究并且一致的编程模型,使开发人员能够更加专注于问题的解决,即为所遇到的问题建立合适的模型,而不是绞尽脑汁地考虑如何将其多线程化。一旦开发人员建立好了模型,稍做修改总是能够方便地映射到Java提供的多线程编程模型上。

4.1.3 线程优先级

现代操作系统基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当线程的时间片用完了就会发生线程调度,并等待着下次分配。线程分配到的时间片多少也就决定了线程使用处理器资源的多少,而线程优先级就是决定线程
需要多或者少分配一些处理器资源的线程属性。

在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配时间片的数量要多于优先级低的线程。设置线程优先级时,针对频繁阻塞(休眠或者IO操作)的线程需要设置较高优先级,而偏重计算(需要较多CPU时间或者偏运算)的线程则设置较低的优先级,确保处理器不会被独占。在不同的JVM以及操作系统上线程规划会存在差异,有些操作系统甚至会忽略对线程优先级的设定。示例代码如下:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Priority {
    private static volatile boolean notStart = true;
    private static volatile boolean notEnd = true;

    public static void main(String[] args) throws Exception {
        List<Job> jobs = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            int priority = i < 5 ? Thread.MIN_PRIORITY : Thread.MAX_PRIORITY;
            Job job = new Job(priority);
            jobs.add(job);
            Thread thread = new Thread(job, "Thread:" + i);
            thread.setPriority(priority);
            thread.start();
        }
        notStart = false;
        TimeUnit.SECONDS.sleep(10);
        notEnd = false;

        for (Job job : jobs) {
            System.out.println("Job Priority : " + job.priority + ", Count : " + job.jobCount);
        }
    }

    static class Job implements Runnable {
        private int priority;
        private long jobCount;

        public Job(int priority) {
            this.priority = priority;
        }

        public void run() {
            while (notStart) {
                Thread.yield();
            }

            while (notEnd) {
                Thread.yield();
                jobCount++;
            }
        }
    }
}

打印:

Job Priority : 1, Count : 4701096
Job Priority : 1, Count : 4519326
Job Priority : 1, Count : 5447983
Job Priority : 1, Count : 5545905
Job Priority : 1, Count : 4658417
Job Priority : 10, Count : 5173026
Job Priority : 10, Count : 6420976
Job Priority : 10, Count : 6464159
Job Priority : 10, Count : 5162462
Job Priority : 10, Count : 5045491

Thread.yield() 方法在Java中是用来使得当前正在执行的线程暂停,并允许其他线程有机会运行。这并不意味着线程终止或进入等待状态,而只是放弃了当前的时间片,让CPU可以调度其他同优先级的线程来运行。
这个方法主要用于线程间的轻微协调,比如在一个线程中频繁检查某个条件是否满足,但又不希望一直占用CPU资源,这时可以让线程调用 yield() 来让出CPU给其他线程,一旦条件满足或者线程调度器重新选择该线程,它将再次开始执行。
需要注意的是,yield() 只会让出同优先级的线程执行,如果当前没有同优先级的可运行线程,那么当前线程会立即重新获得CPU控制权并继续执行。

从输出可以看到线程优先级没有生效,优先级1和优先级10的Job计数的结果非常相近,没有明显差距。这表示程序正确性不能依赖线程的优先级高低。

注意:线程优先级不能作为程序正确性的依赖,因为操作系统可以完全不用理会Java线程对于优先级的设定。书本作者环境是mac,java版本是1.7,所有 Java线程优先级均为5(通过jstack查看),对线程优先级的设置会被忽略。另外,尝试在 Ubuntu 14.04环境下运行该示例,输出结果也表示该环增忽略了线程优先级的设置。

4.1.4 线程的状态

Java线程在运行的生命周期中可能处于下表所示的6种不同的状态,在给定的一个时刻,线程只能处于其中的一个状态。

java线程的状态:

    public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
状态名称说明
NEW初始状态,线程被构建,但是还没有调用start()方法
RUNNABLE运行状态,Java线程将操作系统中的就绪和运行两种状态宠统地称作“运行中”
BLOCKED阻塞状态,表示线程阻于锁
WAITING等待状态,表示线程进人等待状态,进人该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
TIMED_WAITING超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的
TERMINATED终止状态,表示当前线程已经执行完毕

下面我们使用jstack工具(可以选择打开终端,键人jstack或者到JDK安装目录的bin目录下执行命令),尝试查看示例代码运行时的线程信息,更加深人地理解线程状态,示例代码如下:

public class ThreadState {
    public static void main(String[] args) {
        new Thread(new TimeWaiting(), "TimeWaitingThread").start();
        new Thread(new Waiting(), "WaitingThread").start();

        // 使用两个Blocked线程,一个获取锁成功,另一个被阻塞
        new Thread(new Blocked(),"BlockedThread-1").start();
        new Thread(new Blocked(),"BlockedThread-2").start();
    }

    // 该线程不断地进行睡眠
    static class TimeWaiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                SleepUtils.second(100);
            }
        }
    }

    // 该线程在Waiting.class实例上等待
    static class Waiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (Waiting.class) {
                    try {
                        Waiting.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    // 该线程在Blocked.class实例上加锁后,不会释放该锁
    static class Blocked implements Runnable {
        @Override
        public void run() {
            synchronized (Blocked.class) {
                while (true) {
                    SleepUtils.second(100);
                }
            }
        }
    }
}
import java.util.concurrent.TimeUnit;

public class SleepUtils {
    public static final void second(long seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行ThreadState类。

打开cmd,使用jps得到进程号

D:\>jps
14372
14900 Jps
11292 ThreadState
6044 Launcher

使用jstack 进程id查看堆栈信息

D:\>jstack 11292
2024-07-03 00:41:57
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.271-b09 mixed mode):

"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x000002b0fafdc000 nid=0x3f70 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"BlockedThread-2" #15 prio=5 os_prio=0 tid=0x000002b09304c000 nid=0x3078 waiting on condition [0x000000e66a4ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.xin.demo.threaddemo.bookdemo.SleepUtils.second(SleepUtils.java:8)
        at com.xin.demo.threaddemo.bookdemo.ThreadState$Blocked.run(ThreadState.java:45)
        - locked <0x00000000db721998> (a java.lang.Class for com.xin.demo.threaddemo.bookdemo.ThreadState$Blocked)
        at java.lang.Thread.run(Thread.java:748)

"BlockedThread-1" #14 prio=5 os_prio=0 tid=0x000002b093061800 nid=0x27b8 waiting for monitor entry [0x000000e66a3ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.xin.demo.threaddemo.bookdemo.ThreadState$Blocked.run(ThreadState.java:45)
        - waiting to lock <0x00000000db721998> (a java.lang.Class for com.xin.demo.threaddemo.bookdemo.ThreadState$Blocked)
        at java.lang.Thread.run(Thread.java:748)

"WaitingThread" #13 prio=5 os_prio=0 tid=0x000002b09305a800 nid=0x94c in Object.wait() [0x000000e66a2ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000db71e758> (a java.lang.Class for com.xin.demo.threaddemo.bookdemo.ThreadState$Waiting)
        at java.lang.Object.wait(Object.java:502)
        at com.xin.demo.threaddemo.bookdemo.ThreadState$Waiting.run(ThreadState.java:30)
        - locked <0x00000000db71e758> (a java.lang.Class for com.xin.demo.threaddemo.bookdemo.ThreadState$Waiting)
        at java.lang.Thread.run(Thread.java:748)

"TimeWaitingThread" #12 prio=5 os_prio=0 tid=0x000002b093021800 nid=0x2cd4 waiting on condition [0x000000e66a1ff000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at com.xin.demo.threaddemo.bookdemo.SleepUtils.second(SleepUtils.java:8)
        at com.xin.demo.threaddemo.bookdemo.ThreadState$TimeWaiting.run(ThreadState.java:18)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000002b092ed2800 nid=0x3228 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000002b092e12000 nid=0x3ca4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000002b092e09000 nid=0x638 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000002b092e01800 nid=0x409c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000002b092dfc000 nid=0x283c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000002b092df7000 nid=0x2ff8 runnable [0x000000e669afe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000db6afad8> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000db6afad8> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:53)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000002b092864800 nid=0x2d84 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000002b0fe4ae000 nid=0x718 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000002b0fb07b800 nid=0x17e0 in Object.wait() [0x000000e6697fe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000db308ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000db308ee0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000002b0fb075000 nid=0x10fc in Object.wait() [0x000000e6696fe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000db306c00> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000db306c00> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000002b0fe400800 nid=0x3730 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000002b0faff5800 nid=0x1c04 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000002b0faff8000 nid=0xf94 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000002b0faff9000 nid=0x2ba0 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000002b0faffa800 nid=0x3fdc runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000002b0faffc800 nid=0x3fb0 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x000002b0faffd800 nid=0x19e4 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000002b0fb000800 nid=0xebc runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000002b0fb001800 nid=0x3698 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000002b092e35800 nid=0x127c waiting on condition

JNI global references: 12

通过示例,我们了解到Java程序运行中线程状态的具体含义。线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,Java线程状态变迁如下图所示:

在这里插入图片描述

由上图中可以看到,线程创建之后,调用start()方法开始运行。当线程执行 wait()方法之后,线程进人等待状态。进人等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而超时等待状态相当于在等待状态的基础上增加了超时限制,也就是超时时间到达时将会返回到运行状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到阻寨状态。线程在执行Runnable的run()方法之后将会进人到终止状态。

Java将操作系统中的送行和就绪两个状态合并称为运行状态。阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在java.concurrent包中Lock接口的线程状态却是等待状态,因为java.concurrent包中Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。

4.1.5 Daemon 线程

Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以通过调用 Thread.setDaemon(true)将线程设置为Daemon 线程。

注意:Daemon属性需要在启动线程之前设置,不能在启动线程之后设置。

Daemon线程被用作完成支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块并不一定会执行,代码示例如下:

public class Daemon {
    public static void main(String[] args) {
        Thread thread = new Thread(new DaemonRunner(), "DaemonRunner");
        thread.setDaemon(true);
        thread.start();
    }

    static class DaemonRunner implements Runnable {
        @Override
        public void run() {
            try {
                SleepUtils.second(10);
            } finally {
                System.out.println("DaemonThread finally run.");
            }
        }
    }
}

运行Daemon程序,可以看到在终端或者命令提示符上没有任何输出。main线程(非Daemon线程)在启动了线程DaemonRunner之后随着main方法执行完毕而终止,而此时Java虚拟机中已经没有非Daemon线程,虚报机需要退出。Java虚拟机中的所有 Daemon线段都需要立即终止,因此DaemonRunner立即终止,但是DeamonRunner中的在finally块并改有执行。

注意:在构建 Daemon 线程时,不能依靠finally块中的内容来确保执行关闲或清理资源的逻辑。

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

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

相关文章

DC/AC电源模块:为智能家居设备提供恒定的电力供应

BOSHIDA DC/AC电源模块&#xff1a;为智能家居设备提供恒定的电力供应 DC/AC电源模块是一种常见的电源转换器&#xff0c;它将直流电源&#xff08;DC&#xff09;转换为交流电源&#xff08;AC&#xff09;&#xff0c;为智能家居设备提供恒定的电力供应。在智能家居系统中&a…

Linux运维:mysql视图,用户及远程登录,用户密码的修改和破解

目 录 一、视图 二、用户 2.1 新建用户 三、创建远程登录用户test 3.1 远程登录mysql​编辑 3.1 7-1需要赋予权限 3.3 修改远程登录用户的密码 3.4 修改远程登录的用户名 3.5 删除用户&#xff1a;drop user lisi192.168.114.%; 四、修改用户密码 4.1 修改当前本地…

网安小贴士(3)网安协议

一、前言 网络安全协议是构建安全网络环境的基础&#xff0c;它们帮助保护网络通信免受各种威胁和攻击。 二、定义 网络安全协议是指在计算机网络中用于确保网络通信和数据传输安全的协议。它们定义了在网络通信过程中的安全机制、加密算法、认证和授权流程等&#xff0c;以保…

【论文阅读】自动驾驶光流任务 DeFlow: Decoder of Scene Flow Network in Autonomous Driving

再一次轮到讲自己的paper&#xff01;耶&#xff0c;宣传一下自己的工作&#xff0c;顺便完成中文博客的解读 方便大家讨论。 Title Picture Reference and pictures paper: https://arxiv.org/abs/2401.16122 code: https://github.com/KTH-RPL/DeFlow b站视频: https://www.b…

关于 Mybatis 的开启二级缓存返回对象不一致问题

做实验报告的时候&#xff0c;跟着学习&#xff0c;发现我已经将 开启 二级缓存的 配置都配置好了&#xff0c;但是返回值地址不一致&#xff0c;说明对象不一致&#xff0c;二级缓存命中失败。 跟着流程配置&#xff1a; mybatis-config <settings><!-- 启用 myba…

SpringBoot+Thymeleaf项目重定向到另一个系统HTTPS变成HTTP

SpringBootThymeleaf项目是一个简单的单体项目&#xff0c;只有一个页面。 重定向的是前后分离&#xff0c;前端用的vue。 浏览器看到重定向后 https成了http&#xff0c;F12控制台看到是 301 Moved Permanently 单体项目最开始写法&#xff1a; response.sendRedirect(url); …

react native中使用@react-navigation/native进行自定义头部

react native中使用react-navigation/native进行自定义头部 效果示例图实例代码 效果示例图 实例代码 /* eslint-disable react-native/no-inline-styles */ /* eslint-disable react/no-unstable-nested-components */ import React, { useLayoutEffect } from react; import…

基于uniapp(vue3)H5附件上传组件,可限制文件大小

代码&#xff1a; <template><view class"upload-file"><text>最多上传5份附件&#xff0c;需小于50M</text><view class"" click"selectFile">上传</view></view><view class"list" v…

html+css+js气球消除小游戏

气球消除小游戏 消除15个就成功 源码在图片后 点赞加关注&#xff0c;谢谢 左上角的数字显示消除气球的数量 定时随机生成气球 &#x1f388;&#x1f388;&#x1f388; 图片 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset&…

C++部分复习笔记上

C语法复习 1. C入门基础 缺省参数 半缺省参数必须从右往左依次来给出&#xff0c;不能间隔着给缺省参数不能在函数声明和定义中同时出现缺省值必须是常量或者全局变量C语言不支持&#xff08;编译器不支持&#xff09; 函数重载 函数重载是函数的一种特殊情况&#xff0c;…

小试牛刀-Solana合约账户详解

目录 一.Solana 三.账户详解 3.1 程序账户 3.2 系统所有账户 3.3 程序派生账户(PDA) 3.4 Token账户 四、相关学习文档 五、在线编辑器 Welcome to Code Blocks blog 本篇文章主要介绍了 [Solana合约账户详解] ❤博主广交技术好友&#xff0c;喜欢文章的可以关注一下❤ …

企业如何管理安全生产工作?(附模板)

总结一下在企业内管理安全中遇到的一些问题&#xff1a; 1、 管理方式落后&#xff0c;还在使用纸质记录 2、 人员信息杂乱无章&#xff0c;无人整理 3、出现问题找不到源头和负责人 我做系统管理已经7年了&#xff0c;题主说的这些问题我之前也遇到过&#xff0c;相信也有…

LabVIEW幅频特性测试系统

使用LabVIEW软件开发的幅频特性测试系统。该系统整合了Agilent 83732B信号源与Agilent 8563EC频谱仪&#xff0c;通过LabVIEW编程实现自动控制和数据处理&#xff0c;提供了成本效益高、操作简便的解决方案&#xff0c;有效替代了昂贵的专用仪器&#xff0c;提高了测试效率和设…

taro小程序terser-webpack-plugin插件不生效

背景 taro小程序terser-webpack-plugin插件不生效 运行环境&#xff1a; taro3.6.32vue3.2.24 webpack5 不断尝试过 按官网这种配置不生效&#xff0c;然后又看了文档中赋的这个链接《编写插件&#xff0c;将 Taro 编译打包耗时缩短至三分之一》。 按这个文档中的配置&…

魏建军亲测智驾第二季:长城全场景NOA挑战重庆

6月30日&#xff0c;魏建军再次直播长城的全场景智能驾驶NOA。上一次直播是保定&#xff0c;而这次选在了山城重庆。 重庆号称「8D魔幻城市」&#xff0c;是每一个在智驾上有抱负的头部公司&#xff0c;都想跑一跑必争之地。 这次直播长城用的智驾系统&#xff0c;也是TA序列…

Go - 8.func 函数使用

目录 一.引言 二.func 定义 三.func 实践 1.多个返回值 2.命名返回值 3.可变参数 四.总结 一.引言 函数是编程语言中的基本构建块&#xff0c;用于将代码组织成可重用的逻辑单元。函数可以接受输入参数&#xff0c;执行特定的操作&#xff0c;并返回结果。在 Go 语言&a…

25.labview数据采集中的读取和写入文本文件和Excel表格文件

①本文将会讲解labview读取和写入文本文件和Excel文件的几种不同方式&#xff0c;讲解程序的基本原理&#xff0c;并提出具体的实施方案&#xff0c;本文内容如下所示。 ②本文文章结束会提供大家 文本和表格读取写入的源程序 &#xff0c;以便于大家学习和使用。 本文中可能用…

家政小程序的开发:打造现代式便捷家庭服务

随着现代生活节奏的加快&#xff0c;人们越来越注重生活品质与便利性。在这样的背景下&#xff0c;家政服务市场迅速崛起&#xff0c;成为许多家庭日常生活中不可或缺的一部分。然而&#xff0c;传统的家政服务往往存在信息不对称、服务效率低下等问题。为了解决这些问题&#…

Windows10录屏,教你3个方法,简单快速录屏

“我的电脑系统是Windows10的系统&#xff0c;今晚要进行线上开会&#xff0c;但我实在有事没办法参加会议&#xff0c;想把会议的内容录制下来方便我后续观看。但却找不到电脑录屏功能在哪里打开&#xff1f;求助一下&#xff0c;谁能帮帮我&#xff1f;” 在数字化时代&…

Oracle - 数据库打补丁实践

原文&#xff1a;https://www.cnblogs.com/ddzj01/p/12097467.html 一、概述 本文将介绍如何给oracle数据库打最新补丁&#xff0c;数据库版本为11.2.0.4单实例&#xff0c;操作系统为redhat6.5 二、下载相关升级包 1. 登录MOS&#xff0c;查阅(ID 2118136.2)&#xff0c;下载…