并发编程Thread的常用API有哪些?

这篇具有很好参考价值的文章主要介绍了并发编程Thread的常用API有哪些?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在JDK17(或以上版本)中,Thread类提供了一组常用的API,用于管理线程的创建、启动、暂停、恢复和销毁等操作。本文从api、源码、编程示例等方面详细说明Thread常用函数的使用和注意事项。

并发编程Thread的常用API有哪些?

线程 sleep

  • 使当前正在执行的线程暂停(挂起)指定的毫秒数。但受系统计时器和调度程序的精度和准确性限制。
  • 线程不会失去任何monitor(监视器)的所有权。
  • 每个线程的休眠互不影响,Thread.sleep 只会导致当前线程进入指定时间的休眠。
    public static native void sleep(long millis) throws InterruptedException;
    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0 && millis < Long.MAX_VALUE) {
            millis++;
        }

        sleep(millis);
    }

通过测试发现 Thread.sleep 之间互不影响。代码如下:

/**
 * 每个线程的休眠互不影响,`Thread.sleep` 只会导致当前线程进入指定时间的休眠。
 */
public class ThreadSleepTest {
    public static void main(String[] args) throws Exception{
        Thread thread1 = new Thread(()->{
            int i = 0;
            while(i<10){
                System.out.println("thread demo start "+i);
                i++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread1.start();
        System.out.println("thread main start ");
        Thread.sleep(2000);
        System.out.println("thread main end ");
    }
}

输出结果如下:

thread main start 
thread demo start 0
thread demo start 1
thread main end 
thread demo start 2
thread demo start 3

除此之外可以使用 java.util.concurrent.TimeUnit 类来更简单的实现指定时间的休眠,后续源码使用该类来进行休眠线程。例子代码如下:

package engineer.concurrent.battle.abasic;

import java.util.concurrent.TimeUnit;

/**
 * TimeUnit工具类替代Thread.sleep方法。
 * @author r0ad
 * @since 1.0
 */
public class ThreadSleepTimeUnitTest {
    public static void main(String[] args) throws Exception{
        System.out.println("thread main start ");
        TimeUnit.SECONDS.sleep(1);
        TimeUnit.MILLISECONDS.sleep(500);
        TimeUnit.MINUTES.sleep(1);
        System.out.println("thread main end ");
    }
}

/**
 * java.util.concurrent.TimeUnit#sleep 源码,底层实现也是Thread.sleep。
 * Performs a {@link Thread#sleep(long, int) Thread.sleep} using
 * this time unit.
 * This is a convenience method that converts time arguments into the
 * form required by the {@code Thread.sleep} method.
 *
 * @param timeout the minimum time to sleep. If less than
 * or equal to zero, do not sleep at all.
 * @throws InterruptedException if interrupted while sleeping
 */
public void sleep(long timeout) throws InterruptedException {
    if (timeout > 0) {
        long ms = toMillis(timeout);
        int ns = excessNanos(timeout, ms);
        Thread.sleep(ms, ns);
    }
}

线程 yield

Thread.yield()是一个提示,用于告诉调度程序当前线程愿意放弃使用处理器。调度程序可以选择忽略这个提示。Yield是一种试图改善线程之间相对进程的启发式方法,否则它们会过度利用CPU。它的使用应该与详细的分析和基准测试结合起来,以确保它确实产生了预期的效果。

这种方法适用场景很少。它有助于调试或测试,以帮助重现由于竞态条件而引起的错误。在设计并发控制结构时,例如java.util.concurrent.locks包中的结构,它也可能有用。

调用Thread.yield()函数会将当前线程从RUNNING状态切换到RUNNABLE状态。

    public static native void yield();

测试代码如下,在cpu资源不紧张的情况下输出仍然是乱序的。

package engineer.concurrent.battle.abasic;

import java.util.stream.IntStream;

/**
 * ThreadYield测试
 * @author r0ad
 * @since 1.0
 */
public class ThreadYieldTest {
    public static void main(String[] args) throws Exception{
        System.out.println("thread main start ");
        IntStream.range(0, 2).mapToObj(ThreadYieldTest::create).forEach(Thread::start);
        System.out.println("thread main end ");
    }
    private static Thread create(int i) {
        return new Thread(() -> {
            if(i == 0 ){
                Thread.yield();
            }
            System.out.println("thread " + i + " start ");
        });
    }
}

输出结果:

thread main start 
thread main end 
thread 0 start 
thread 1 start 

Thread.yield()Thread.sleep() 方法之间的联系和差异如下:

联系:

  • Thread.yield() 和 Thread.sleep() 都会使当前线程暂停执行,让出CPU资源给其他线程。
  • Thread.yield() 和 Thread.sleep() 都不会释放当前线程所占用的锁。

差异:

  • Thread.yield() 方法只是暂停当前线程的执行,让出CPU资源给其他线程,但不会进入阻塞状态。可能导致CPU进行上下文切换。
  • Thread.sleep() 方法会使当前线程暂停指定的时间,并进入阻塞状态,直到休眠时间结束或者被其他线程打断。
  • Thread.sleep()具有较高的可靠性,可以确保至少暂停指定的时间。Thread.yield()则不能保证暂停。

设置线程的优先级

  • java.lang.Thread#setPriority 修改线程的优先级
  • java.lang.Thread#getPriority 获取线程的优先级

java.lang.Thread#setPriority 修改线程的优先级实现过程如下:

  • 调用此线程的checkAccess方法,不带任何参数。这可能会导致抛出一个SecurityException异常。
  • 线程的优先级被设置为指定的newPriority和线程所属线程组允许的最大优先级中较小的值。

    /**
     * `java.lang.Thread#setPriority` 修改线程的优先级源码
     */
    public final void setPriority(int newPriority) {
        ThreadGroup g;
        // 调用此线程的`checkAccess`方法,不带任何参数。这可能会导致抛出一个`SecurityException`异常。
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            // 线程的优先级被设置为指定的`newPriority`和线程所属线程组允许的最大优先级中较小的值。
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    /**
     * `java.lang.Thread#getPriority` 获取线程的优先级
     */
    public final int getPriority() {
        // 返回Thread的priority属性
        return priority;
    }
    /* 原生优先级设置方法 */
    private native void setPriority0(int newPriority);

进程有进程的优先级,线程同样也有优先级,理论上是优先级比较高的线程会获取优先被 CPU 调度的机会,但是事实上设置线程的优先级同样也是一个 hint 操作,具体如下。

  • 对于 root 用户,它会 hint 操作系统你想要设置的优先级别,否则它会被忽略。
  • 如果 CPU 比较忙,设置优先级可能会获得更多的 CPU 时间片,但是闲时优先级的高低几乎不会有任何作用。

所以不要使用线程的优先级进行某些特定业务的绑定,业务执行的顺序应该还是要使用同步执行方法来保证。

测试例子如下,线程之间会交替输出:

package engineer.concurrent.battle.abasic;

/**
 * ThreadPriorityTest测试
 * @author r0ad
 * @since 1.0
 */
public class ThreadPriorityTest {
    public static void main(String[] args) throws Exception{
        Thread t1 = ThreadPriorityTest.create("t1");
        t1.setPriority(1);
        Thread t2 = ThreadPriorityTest.create("t2");
        t2.setPriority(10);
        t1.start();
        t2.start();
    }
    private static Thread create(String name) {
        return new Thread(() -> {
            while (true) {
                System.out.println("thread " + name );
            }
        });
    }
}

获取线程ID

返回此线程的标识符。线程ID是一个正的long数字,在创建此线程时生成。线程ID是唯一的,并在其生命周期内保持不变。当一个线程终止时,该线程ID可能会被重新使用。

    public long getId() {
        return tid;
    }

获取当前线程

java.lang.Thread#currentThread 方法被大多数框架使用,像是SpringMVC、MyBatis这些。调用该函数会返回当前正在执行的线程对象。

    @IntrinsicCandidate
    public static native Thread currentThread();

测试代码如下:

package engineer.concurrent.battle.abasic;

/**
 * ThreadCurrentTest测试
 * @author r0ad
 * @since 1.0
 */
public class ThreadCurrentTest {
    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println(this == Thread.currentThread());
            }
        };
        t1.start();
        System.out.println(Thread.currentThread().getName());
    }
}

设置线程上下文类加载器

  • java.lang.Thread#getContextClassLoader 返回该线程的上下文ClassLoader。上下文ClassLoader由创建线程的对象提供,用于在此线程中运行的代码在加载类和资源时使用。如果未设置(通过setContextClassLoader()方法),则默认为父线程的ClassLoader上下文。原始线程的上下文ClassLoader通常设置为用于加载应用程序的类加载器。
  • java.lang.Thread#setContextClassLoader 设置此线程的上下文ClassLoader。上下文ClassLoader可以在创建线程时设置,并允许线程的创建者通过getContextClassLoader方法为在线程中运行的代码提供适当的类加载器,用于加载类和资源。如果存在安全管理器,则会使用其checkPermission方法,传入RuntimePermissionsetContextClassLoader权限,以检查是否允许设置上下文ClassLoader。
    @CallerSensitive
    public ClassLoader getContextClassLoader() {
        if (contextClassLoader == null)
            return null;
        @SuppressWarnings("removal")
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader.checkClassLoaderPermission(contextClassLoader,
                                                   Reflection.getCallerClass());
        }
        return contextClassLoader;
    }

    public void setContextClassLoader(ClassLoader cl) {
        @SuppressWarnings("removal")
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission("setContextClassLoader"));
        }
        contextClassLoader = cl;
    }

线程 interrupt

  • java.lang.Thread#interrupt

中断此线程。除非当前线程自己中断自己,这是始终允许的,否则会调用该线程的checkAccess方法,可能会引发SecurityException异常。

如果此线程在Object类的wait()wait(long)wait(long, int)方法的调用中被阻塞,或者在此类的join()join(long)join(long, int)sleep(long)sleep(long, int)方法的调用中被阻塞,则它的中断状态将被清除,并且它将收到一个InterruptedException异常。
如果此线程在对InterruptibleChannel的I/O操作中被阻塞,则通道将被关闭,线程的中断状态将被设置,并且线程将收到一个ClosedByInterruptException异常。

如果此线程在Selector中被阻塞,则线程的中断状态将被设置,并且它将立即从选择操作中返回,可能带有非零值,就像调用了选择器的wakeup方法一样。

如果以上条件都不满足,则将设置此线程的中断状态。

中断一个未启动的线程可能不会产生任何效果。在JDK参考实现中,中断一个未启动的线程仍然记录了中断请求的发出,并通过interruptedisInterrupted()方法报告它。

  • java.lang.Thread#interrupted

测试当前线程是否已被中断。此方法将清除线程的"中断状态"。换句话说,如果连续两次调用此方法,第二次调用将返回false(除非在第一次调用清除了线程的中断状态之后,而第二次调用在检查之前再次中断了当前线程)。

  • java.lang.Thread#isInterrupted

测试此线程是否已被中断。此方法不会影响线程的"中断状态"。

    public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupted = true;
                    interrupt0();  // inform VM of interrupt
                    b.interrupt(this);
                    return;
                }
            }
        }
        interrupted = true;
        // inform VM of interrupt
        interrupt0();
    }

    public static boolean interrupted() {
        Thread t = currentThread();
        boolean interrupted = t.interrupted;
        // We may have been interrupted the moment after we read the field,
        // so only clear the field if we saw that it was set and will return
        // true; otherwise we could lose an interrupt.
        if (interrupted) {
            t.interrupted = false;
            clearInterruptEvent();
        }
        return interrupted;
    }

    public boolean isInterrupted() {
        return interrupted;
    }

测试代码如下:

package engineer.concurrent.battle.abasic;

import java.util.concurrent.TimeUnit;

/**
 * ThreadInterruptTest
 * @author r0ad
 * @since 1.0
 */
public class ThreadInterruptTest {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                int i = 0;
                while(i<10){
                    i++;
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t1.start();
        // 中断该线程
        t1.interrupt();
        System.out.println("t1 interrupt status " + t1.isInterrupted());
        System.out.println("t1 is interrupted and I can work still. ");
        // 修改中断状态,但是线程不会继续执行
        t1.isInterrupted();
        System.out.println("t1 interrupt status " + t1.isInterrupted());
    }
}

线程 join

Thread 的 join 方法同样是一个非常重要的方法,与 sleep 一样它也是一个可中断的方法。Thread类通过重载实现了三个函数供多线程开发使用。

  • java.lang.Thread#join(long)

等待最多millis毫秒,让此线程死亡。0的超时时间意味着永久等待。此实现使用了一个基于this.isAlive条件的this.wait调用循环。当线程终止时,将调用this.notifyAll方法。建议应用程序不要在Thread实例上使用wait、notify或notifyAll。

  • java.lang.Thread#join(long, int)

等待最多 millis 毫秒加上 nanos 纳秒以使此线程死亡。如果两个参数都是 0,那么意味着永远等待。此实现使用一个循环的 this.wait 调用,条件为 this.isAlive。当一个线程终止时,会调用 this.notifyAll 方法。建议应用程序不要在 Thread 实例上使用 wait、notify 或 notifyAll。

  • java.lang.Thread#join()

等待此线程终止。调用此方法的行为与调用 join(0) 完全相同。

源码实现如下:

    public final synchronized void join(final long millis)
    throws InterruptedException {
        if (millis > 0) { 
            if (isAlive()) {
                final long startTime = System.nanoTime();
                long delay = millis;
                do {
                    wait(delay);
                } while (isAlive() && (delay = millis -
                        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
            }
        } else if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative");
        }
    }

    public final synchronized void join(long millis, int nanos)
    throws InterruptedException {

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0 && millis < Long.MAX_VALUE) {
            millis++;
        }

        join(millis);
    }

    public final void join() throws InterruptedException {
        join(0);
    }

当调用join函数之后主线程和子线程的状态切换如下:

  • 当调用join()方法时,主线程会进入等待状态,直到子线程执行完毕后才会继续执行。此时主线程的状态为WAITING。
  • 如果调用带参数的join()方法,主线程会在等待一段时间后继续执行,而不必一直阻塞。在这种情况下,主线程的状态为TIMED_WAITING。
  • 如果子线程已经执行完毕,但是主线程还没有调用join()方法,则子线程的状态为TERMINATED,而主线程的状态为RUNNABLE。
  • 如果主线程调用join()方法等待子线程完成执行,而子线程抛出了异常,则主线程会收到异常信息并抛出InterruptedException异常。

测试代码如下:

package engineer.concurrent.battle.abasic;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * ThreadJoinTest
 * @author r0ad
 * @since 1.0
 */
public class ThreadJoinTest {
    public static void main(String[] args) throws InterruptedException{
        List<Thread> threadList = IntStream.range(1, 10).mapToObj(ThreadJoinTest::create).toList();
        threadList.forEach(Thread::start);
        for(Thread thread : threadList){
            thread.join();
        }
        IntStream.range(1, 10).forEach((i)-> {
            System.out.println("thread " + Thread.currentThread().getName() + " # "+ i );
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 线程创建函数
     * @param index
     * @return
     */
    private static Thread create(int index) {
        return new Thread(() -> {
            int i = 0;
            while (i++<10) {
                System.out.println("thread " + Thread.currentThread().getName() + " # "+ i );
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

通过观察输出结果发现,join之后的线程全部结束后才会执行输出main线程的内容。

thread Thread-0 # 10
thread Thread-1 # 10
thread Thread-7 # 10
thread Thread-6 # 10
thread main # 1
thread main # 2
thread main # 3

关闭线程

在JDK 17中,线程停止的情况和函数有以下几种:

  1. 自然结束:线程执行完run()方法后,线程会自然结束并进入终止状态。
  2. 线程被中断:可以使用Thread类的interrupt()方法来中断线程。当一个线程调用另一个线程的interrupt()方法时,被调用线程会收到一个中断信号,并且中断状态会被设置为true。中断状态可以通过Thread类的isInterrupted()方法来查询。线程可以在适当的时机检查中断状态,如果中断状态为true,则可以选择安全地终止线程的执行。
  3. 使用标志位停止线程:可以在多线程程序中定义一个标志位,当标志位为true时,线程停止执行。线程可以周期性地检查该标志位,如果标志位为true,则主动结束线程的执行。
  4. 使用Thread类的stop()方法(已废弃):Thread类提供了一个stop()方法,可以立即停止线程的执行。但是这个方法已经被标记为不安全和不推荐使用,因为它可能导致线程在不可预料的位置停止,造成数据不一致或其他问题。

tips native函数

Java中的native关键字用于表示某个方法的实现是由本地代码(C、C++等)提供的。这些本地方法可以直接在Java程序中调用,而无需了解其底层实现。

在Java中,使用native关键字定义本地方法时,不需要提供方法体。例如:

public native void myNativeMethod();

在上面的示例中,myNativeMethod()被定义为本地方法,并且没有提供方法体。在运行时,Java虚拟机将查找本地方法的实现,如果找不到,则会抛出UnsatisfiedLinkError异常。

要调用本地方法,需要使用native方法的外部实现。这通常涉及到将Java代码与本地代码库进行链接。可以使用Java本机接口(JNI)来实现这一点。

参考

  • 《Java高并发编程详解:多线程与架构设计》
  • Java Thread Doc

关于作者

来自一线全栈程序员nine的八年探索与实践,持续迭代中。欢迎关注“雨林寻北”或添加个人卫星codetrend(备注技术)。文章来源地址https://www.toymoban.com/news/detail-837904.html

到了这里,关于并发编程Thread的常用API有哪些?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 掌握JDK21全新结构化并发编程,轻松提升开发效率!

    通过引入结构化并发编程的API,简化并发编程。结构化并发将在不同线程中运行的相关任务组视为单个工作单元,从而简化错误处理和取消操作,提高可靠性,并增强可观察性。这是一个预览版的API。 结构化并发是由JEP 428提出的,并在JDK 19中作为孵化API发布。它在JDK 20中被

    2024年02月12日
    浏览(35)
  • Java 17官方编程手册都针对哪些方面做了更新?

    Java 17,官方编程手册, 《International Developer》杂志称为“全世界醉著名的编程书籍创作者之一”的Herbert Schildt倾情解读 《Java官方编程手册》从1996年首次出版以来,已经经历了数次改版,每次改版都反映 了Java不断演化的进程。本书是第12版,针对JavaSE 17(JDK 17)进行了更新。因

    2024年02月11日
    浏览(34)
  • React Router,常用API有哪些?

    React Router是一个用于构建单页面应用程序(SPA)的库,它是用于管理React应用中页面导航和路由的工具。SPA是一种Web应用程序类型,它在加载初始页面后,通过JavaScript来动态加载并更新页面内容,而不是在每次页面切换时都请求完整的新页面。通过React Router可以在React应用中

    2024年02月07日
    浏览(29)
  • Java常用类之 JDK 8之前的日期时间API 和 8中新日期时间API

    Java常用类 JDK 8 之前日期和时间的API测试: 详细代码如下: SimpleDateFormate的使用: SimpleDateFormate对日期Date类的格式化和解析 重点关注: 具体代码如下: 1.获取Calendar实例的方法 2.重点掌握几个常用方法: 注意: 获取月份时:一月是0,二月是1,以此类推,12月是11 获取星期

    2024年02月07日
    浏览(38)
  • JUC并发编程学习笔记(七)常用的辅助类

    CountDownLatch 这是一个JUC计数器辅助类,计数器有加有减,这是减。 使用方法 使用前 可能会在所有人没出去之前关门 使用后 不在乎谁先出去,但是一定要总数等于0后才会关门 原理 countDownLatch.countDown();//总数减1 countDownLatch.await();//等待总数变为0才会往下执行,相当于阻塞当

    2024年02月06日
    浏览(32)
  • 【JavaEE】Servlet 中常用API有哪些?前后端交互方式有哪些?

      博主简介:想进大厂的打工人 博主主页: @xyk: 所属专栏: JavaEE初阶   目录 一、Servlet 运行原理 二、Servlet常用API 2.1 HttpServlet(抽象类) 2.1.1. init 方法 2.1.2 service方法 2.1.3 destroy方法 三、HttpServletRequest 3.1 HttpServletRequest常用方法演示 四、前端给后端传输数据的三种方式 4.

    2024年02月13日
    浏览(38)
  • Java的异常处理、注解、Lambda表达式、流式API 、并发编程示例

    Java 中的异常处理机制可以帮助我们处理程序中出现的异常情况,保证程序的正常运行。在 Java 中,异常被分为两种类型:受检异常和非受检异常。受检异常需要在代码中明确处理,而非受检异常则不需要。Java 中的异常处理主要是通过 try-catch-finally 语句块来实现的。 以下是

    2023年04月16日
    浏览(29)
  • 掌握Java JDK 1.8 API帮助文档中文版,事半功倍编程

    引言: Java是一门强大且广泛应用的编程语言,在开发过程中,准确地了解和使用Java标准库的API是非常重要的。JDK(Java Development Kit)是Java开发工具包,提供了丰富的API文档,其中包含了Java标准库中各种类和方法的详细说明。本文将为您介绍如何使用JDK 1.8 API帮助文档的中文

    2024年02月07日
    浏览(31)
  • 【第十七节:微信小程序 常用api】微信小程序入门,以思维导图的方式展开17

    如果看不清大图,可以私信五木大大发高清图哈。      wx.request(OBJECT)         发起的是 HTTPS 请求         url    String    是    开发者服务器接口地址         data    Object、String    否    请求的参数         header    Object    否    设置请求的 header ,

    2024年02月03日
    浏览(30)
  • 什么是硬件编程,C 语言如何进行硬件编程?C 语言常用的操作系统有哪些?

    硬件编程是指使用编程语言与硬件交互,控制硬件设备的行为和功能。其中,C语言是一种广泛用于硬件编程的高级编程语言。 C语言通过直接操作内存来进行硬件编程。它提供了一些特殊的和库函数,使得开发者可以直接访问和操作硬件设备的寄存器、端口和外设等。

    2024年02月15日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包