多线程学习篇二:Thread常见方法

news2024/11/14 1:56:41

1. 常见方法

方法名

static

功能说明

注意点

start()

启动一个新线程,在新线程里面运行run方法

start 方法只是让线程进入就绪,里面代码不一定立刻运行(CPU 的时间片还没分给它)。每个线程对象的 start 方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException

run()

新线程启动后会调用的方法

如果在构造 Thread 对象时传递了 Runnable 参数,则线程启动后会调用Runnable 中的 run 方法,否则默认不执行任何操作。但可以创建Thread的子类对象,来覆盖默认行为

join()

等待线程运行结束

join(long n)

等待线程运行结束,最多等待n毫秒

getId()

获取线程长整型的id

id 唯一

getName()

获取线程名

setName(String)

设置线程名

getPriority()

获取线程优先级

setPriority(int)

修改线程优先级

Java 中规定线程优先级是1-10的整数,较大的优先级
能提高该线程被 CPU调度的机率

getState()

获取线程状态

Java 中线程状态有6个,分别为:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED

isInterrupted()

判断是否被打断

不会清除打断标记

interrupt()

打断线程

如果被打断线程正在 sleep 、wait 、join 会导致被打断的线程抛出InterruptedException,并清除打断标记。如果打断的正在运行的线程,则会设置打断标记。park的线程被打断,也会设置打断标记

interrupted()

static

判断当前线程是否被打断

会清除打断标记

isAlive()

线程是否存活

sleep(long n)

static

让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程

yield()

static

提示线程调度器让出当前线程对 CPU 的使用

主要是为了测试和调试

currentThread()

static

获取当前正在执行的线程

2. 方法演示

2.1 setName 与 getName

2.1.1 默认情况下的线程名
@Slf4j
public class Test01 {

    public static void main(String[] args) {

        new Thread(() -> log.info(Thread.currentThread().getName())).start();

        log.info(Thread.currentThread().getName());
    }

}

默认情况下,主线程名称为 main,其他线程名为 Thread-n 的形式,其中 n 为从 0 开始的整型,可以通过 setName 方法进行修改

2.1.2 修改默认线程名
@Slf4j
public class Test02 {

    public static void main(String[] args) {

        Thread.currentThread().setName("m1");

        Thread thread = new Thread(() -> log.info(Thread.currentThread().getName()), "t1");

        thread.start();

        log.info(Thread.currentThread().getName());
    }
}

自定义线程名设置成功

2.2 run 与 start

2.2.1 执行 start 方法
@Slf4j
public class Test03 {

    public static void main(String[] args) {
        Thread thread = new Thread(() -> log.info(Thread.currentThread().getName()));
        thread.start();
    }
}

在新线程中执行 run 方法的代码逻辑

 2.2.2 执行 run 方法
@Slf4j
public class Test03 {

    public static void main(String[] args) {
        Thread thread = new Thread(() -> log.info(Thread.currentThread().getName()));
        thread.run();
    }
}

在主线程中执行 run 方法的代码逻辑,相当于普通方法

2.3 sleep 与 yield

2.3.1 sleep
  • 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
  • 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出InterruptedException
  • 睡眠结束后的线程未必会立刻得到执行
  • 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
2.3.2 yield
  • 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
  • 具体的实现依赖于操作系统的任务调度器
2.3.3 sleep方法的应用 (限制对CPU的使用)
2.3.3.1 不使用 sleep 方法,cpu的使用情况
public class Test04 {

    public static void main(String[] args) {

        new Thread(() -> {
            while (true) {
//                try {
//                    Thread.sleep(50);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
            }
        }).start();

    }
}

.java 源文件未指定 package,并且 CLASSPATH 存在 .:(当前路径),执行以下步骤:

  1. 将文件 Test04.java 上传到 Linux 服务器 
  2. 执行命令 javac Test04.java (编译 .java 文件)
  3. 执行命令 java Test04 (运行 Test04 的 main 方法)

如果指定了package,执行以下步骤:

  1. 将文件 Test04.java 上传到 Linux 服务器 
  2. 执行命令 javac Test04.java -d ./ (编译 .java 文件)
  3. 执行命令 java 类的全路径.Test04 (运行 Test04 的 main 方法)

通过 top 命令查看运行结果

CPU 占用飙升到 100%

2.3.3.2 不使用 sleep 方法(将相关注释去除),cpu的使用情况
public class Test04 {

    public static void main(String[] args) {

        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

    }
}

通过 top 命令查看运行结果

使用了 sleep 方法,尽管睡眠时间很短,但是 CPU 占用锐减

2.4 线程优先级

  • 线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
2.4.1 现象演示
@Slf4j
public class Test05 {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                log.info(Thread.currentThread().getName() + ":" + i);
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                log.info(Thread.currentThread().getName() + ":" + i);
            }
        }, "t2");

        t1.setPriority(10);
        t2.setPriority(1);

        t1.start();
        t2.start();
    }
}

将 t1 的优先级设置到最大值 10,将 t2 的优先级设置到最大值 1

即使 t2 线程的优先级为最小值 1 ,也是有机会优先执行完

2.5 join

2.5.1 执行下列代码,RESULT 的结果是多少?
@Slf4j
public class Test06 {

    private static int RESULT = -1;

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            // 模拟业务处理逻辑
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(3));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            RESULT = 1;

        }, "t1");

        t1.start();

        log.info("result : " + RESULT);
    }
}

运行结果为: -1

2.5.2 使用 sleep 方法行不行,为什么?

使用 sleep 方法不太合适,sleep 时间过长影响效率,sleep 时间过短拿不到正确结果

2.5.3 使用 join 方法

@Slf4j
public class Test06 {

    private static int RESULT = -1;

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            // 模拟业务处理逻辑
            try {
                TimeUnit.SECONDS.sleep(new Random().nextInt(3));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            RESULT = 1;

        }, "t1");

        t1.start();
        t1.join();

        log.info("result : " + RESULT);
    }
}

运行结果为: 1

2.5.3 join 方法耗时分析
2.5.3.1 案例1
@Slf4j
public class Test07 {

    private static int R1 = -1;
    private static int R2 = -1;

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            R1 = 1;

        }, "t1");

        Thread t2 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            R2 = 2;
        }, "t2");

        long start = System.currentTimeMillis();

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        long end = System.currentTimeMillis();

        log.info("R1: {} R2: {} cost: {}s", R1, R2, (end - start) / 1000);

    }
}

一共耗时 2 s,图解如下:

运行到 t1 的 join 方法时,t1 运行结束,t2 已运行 1s,所以还需要等到 1s 就可以运行完毕

2.5.3.2 案例2

将 t1.join() 放入到线程 t2中

@Slf4j
public class Test08 {

    private static int R1 = -1;
    private static int R2 = -1;

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            R1 = 1;

        }, "t1");

        Thread t2 = new Thread(() -> {

            try {
                t1.join();
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            R2 = 2;
        }, "t2");

        long start = System.currentTimeMillis();

        t1.start();
        t2.start();

        t2.join();

        long end = System.currentTimeMillis();

        log.info("R1: {} R2: {} cost: {}s", R1, R2, (end - start) / 1000);

    }
}

一共耗时 3 s,图解如下(假设 t1 优先获得时间片): 

t2 线程需要等到 t1 线程执行完毕才可以执行,相当于同步执行 

2.5.4 有时间的 join
2.5.4.1 join 时间小于业务处理时间
@Slf4j
public class Test09 {

    private static int RESULT = -1;

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            // 模拟业务处理逻辑
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            RESULT = 1;

        }, "t1");

        t1.start();
        t1.join(1000);

        log.info("result : " + RESULT);
    }
}

获取默认值:-1

2.5.4.2 join 时间大于业务处理时间
@Slf4j
public class Test09 {

    private static int RESULT = -1;

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            // 模拟业务处理逻辑
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            RESULT = 1;

        }, "t1");

        t1.start();
        t1.join(2500);

        log.info("result : " + RESULT);
    }
}

获取处理后的值:1

2.6 interrupt、isInterrupted、interrupted

2.6.1 打断阻塞线程

sleep,wait,join 方法都会让线程进入阻塞状态,我们以 sleep 为例

@Slf4j
public class Test10 {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            // 模拟业务处理逻辑
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        t1.interrupt();

        log.info(" 打断状态: {}", t1.isInterrupted());
    }
}

运行结果:打断一个正在阻塞的线程,会抛出 InterruptedException,并且 interrupt 方法会清除打断标记 

PS:主线程的 sleep 时间和 t1 现成的睡眠差距稍微大点,否则 t1 线程就可能处于非阻塞状态,isInterrupted 方法将会返回 true

2.6.2 打断正常线程
@Slf4j
public class Test11 {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                boolean interrupted = current.isInterrupted();
                if (interrupted) {
                    log.debug(" 打断状态: {}", interrupted);
                    break;
                }
            }
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        t1.interrupt();
    }
}

运行结果:打断一个正在运行的的线程,isInterrupted 方法将会返回 true

2.6.3 打断 park 线程
@Slf4j
public class Test12 {

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            log.info("park...");
            LockSupport.park();
            log.info("打断状态:{}", Thread.currentThread().isInterrupted());
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        t1.interrupt();
    }
}

运行结果:打断一个正在 park 的的线程,isInterrupted 方法将会返回 true

如果一个线程的打断状态已经为 true,会发生什么现象?
@Slf4j
public class Test13 {

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                log.info("park...");
                LockSupport.park();
                log.info("打断状态:{}", Thread.currentThread().isInterrupted());
            }
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        t1.interrupt();
    }
}

运行结果:通过打印日志的时间戳,我们可以看出来,如果一个线程的打断标记为 true,则 park 失效

将方法 isInterrupted 改成 interrupted
@Slf4j
public class Test13 {

    public static void main(String[] args) throws Exception {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                log.info("park...");
                LockSupport.park();
                log.info("打断状态:{}", Thread.interrupted());
            }
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        t1.interrupt();
    }
}

运行结果:t1 线程又被 park 住了

2.6.4 相关方法的应用 -- 两阶段终止模式
2.6.4.1 Two Phase Termination

在一个线程 T1 中如何 “优雅” 终止线程 T2?这里的 “优雅” 指的是给 T2 一个 “料理后事” 的机会。

2.6.4.2 错误思路
  • 使用线程对象的 stop() 方法停止线程:stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁
  • 使用 System.exit(int) 方法停止线程:目的仅是停止一个线程,但这种做法会让整个程序都停止
2.6.4.3 优雅停止
2.6.4.3.1 利用 isInterrupted
@Slf4j
public class TwoPhaseTermination01 {

    private Thread thread;

    public void start() {
        thread = new Thread(() -> {
            while (true) {
                Thread currentThread = Thread.currentThread();
                boolean interrupted = currentThread.isInterrupted();
                if (interrupted) {
                    log.info("料理后事");
                    break;
                }
                try {
                    // 睡眠1s
                    TimeUnit.MILLISECONDS.sleep(1000);
                    log.info("保存数据");
                } catch (InterruptedException e) {
                    currentThread.interrupt();
                }
            }
        }, "监控线程");

        thread.start();
    }

    public void stop() {
        thread.interrupt();
    }

    public static void main(String[] args) throws Exception {
        TwoPhaseTermination01 twoPhaseTermination01 = new TwoPhaseTermination01();
        twoPhaseTermination01.start();

        TimeUnit.MILLISECONDS.sleep(3500);
        log.info("stop");
        twoPhaseTermination01.stop();
    }
}

图解流程如下:

2.6.4.3.2 利用停止标记
@Slf4j
public class TwoPhaseTermination02 {

    private Thread thread;

    // 停止标记用 volatile 是为了保证该变量在多个线程之间的可见性
    private volatile boolean stop = false;

    public void start() {
        thread = new Thread(() -> {
            while (true) {
                if (stop) {
                    log.info("料理后事");
                    break;
                }
                try {
                    // 睡眠1s
                    TimeUnit.MILLISECONDS.sleep(1000);
                    log.info("保存数据");
                } catch (InterruptedException ignore) {

                }
            }
        }, "监控线程");

        thread.start();
    }

    public void stop() {
        stop = true;
        thread.interrupt();
    }

    public static void main(String[] args) throws Exception {
        TwoPhaseTermination02 twoPhaseTermination02 = new TwoPhaseTermination02();
        twoPhaseTermination02.start();

        TimeUnit.MILLISECONDS.sleep(3500);
        log.info("stop");
        twoPhaseTermination02.stop();
    }
}

 图解流程如下:

2.7 getStatus

各种状态代码演示

@Slf4j
public class Test14 {

    public static void main(String[] args) throws Exception {

        synchronized (Test14.class) {

            Thread t1 = new Thread(() -> log.info(Thread.currentThread().getName()), "t1");

            Thread t2 = new Thread(() -> {
                while (true) {

                }
            }, "t2");

            Thread t3 = new Thread(() -> {
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "t3");

            Thread t4 = new Thread(() -> {
                try {
                    t2.join();
                    log.info(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }, "t4");

            Thread t5 = new Thread(() -> {
                synchronized (Test14.class) {
                    log.info(Thread.currentThread().getName());
                }
            }, "t5");

            Thread t6 = new Thread(() -> log.info(Thread.currentThread().getName()), "t6");

            t2.start();
            t3.start();
            t4.start();
            t5.start();
            t6.start();

            TimeUnit.MILLISECONDS.sleep(500);

            log.info("t1 state {}", t1.getState());
            log.info("t2 state {}", t2.getState());
            log.info("t3 state {}", t3.getState());
            log.info("t4 state {}", t4.getState());
            log.info("t5 state {}", t5.getState());
            log.info("t6 state {}", t6.getState());
        }
    }
}

2.8 isAlive
@Slf4j
public class Test15 {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);
                log.info(Thread.currentThread().getName());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1");

        t1.start();

        TimeUnit.MILLISECONDS.sleep(500);

        log.info("thread isAlive " + t1.isAlive());

        t1.join();

        log.info("thread isAlive " + t1.isAlive());
    }
}

得出结论:线程 t1 未执行完是存活的,当执行完业务逻辑方法返回 false 

2.9 守护线程
t1、t2 都是非守护线程
@Slf4j
public class Test16 {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                log.info("t1 => " + i);
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 50; i++) {
                log.info("t2 => " + i);
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

当 t1、t2 都是非守护线程,必须等到 t1、t2 都执行完才会退出 JVM

t1 是非守护线程,t2 是守护线程
@Slf4j
public class Test16 {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                log.info("t1 => " + i);
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            for (int i = 1; i <= 50; i++) {
                log.info("t2 => " + i);
            }
        }, "t2");

        t2.setDaemon(true);

        t1.start();
        t2.start();
    }
}

t1 是非守护线程, t2 是守护线程,当  t1 执行完 JVM 中不存在非守护线程,即使 t2 未执行完,也会退出 JVM

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

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

相关文章

man命令详解

一、man命令简介&#xff1a; man是manual的缩写。操作手册之意。 本地的帮助文档称为man pages&#xff0c;这些操作手册随着软件安装而安装到本地&#xff0c;可以使用man命令进行查询。 随着软件包的安装有些操作手册会以文档的方式放在/usr/share/doc目录当中。…

初识HTTP

1、请求头中存储的是该请求的一些主要说明 accept:浏览器通过这个头告诉服务器&#xff0c;它所支持的数据类型 Accept-Charset:浏览器通过这个头告诉服务器&#xff0c;它支持哪种字符集 Accept-Encoding:浏览器通过这个头告诉服务器&#xff0c;支持的压缩格式 Accept-Lan…

微信这些危险设置一定要关!

你有仔细研究你的微信吗&#xff1f; 用了这么久的微信&#xff0c;才知道 有这么几个设置需要及时关闭&#xff01; 逐个操作更安心1️⃣取消不常用的免密支付和自动续费 我>服务>钱包>底部 支付设置>免密支付/自动续费2️⃣取消对附近陌生人授权位置 2️⃣取消对…

2024最新最全:SQL注入漏洞原理及利用方式

1.SQL注入 原理&#xff1a; 在数据交互中&#xff0c;前端的数据传入到后台处理时&#xff0c;由于后端没有做严格的判断&#xff0c;导致其传入的恶意“数据”拼接到SQL语句中后&#xff0c;被当作SQL语句的一部分执行。漏洞产生于脚本&#xff0c;注入是针对数据库进行。 …

卷轴模式系统中的任务起源探索与趣味性设计策略分析

卷轴模式系统中的“卷轴任务”是一种通过用户参与特定任务来获得奖励的机制&#xff0c;旨在增加用户的参与度和活跃度。下面我们将分析卷轴任务的起源和发展&#xff0c;以及如何通过趣味性设计来提升用户体验。 一、卷轴任务的起源与发展 1. 起源 卷轴任务的概念最早可以追…

​2024年最新python教程全套,现在分享给大家(python全栈)

加V备注&#xff1a;“教程”领取python零基础入门教程以及1节免费公开课 全套学习资料内容分享&#xff1a; 一、Python全面学习方向路线 该内容详细整理了所有Python学习中常用到的技术点&#xff0c;汇总了各个领域的知识点&#xff0c;你可以用它来精准的寻找到对应的学…

springboot阿尔茨海默病预防网站-计算机毕业设计源码77742

目录 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2网站分析 2.1 可行性分析 2.2 网站流程分析 2.2.1网站开发流程 2.2.2 用户登录流程 2.2.3 网站操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 网站功能分析 …

一个开源的大语言模型(LLM)服务工具,支持Llama 3.1、Phi 3、Mistral、Gemma 2 等, 87.4k star你必须拥有(附源码)

这一年来&#xff0c;AI 发展的越来越快&#xff0c;大模型使用的门槛也越来越低&#xff0c;每个人都可以在自己的本地运行大模型。之前也给大家介绍过一些可以在本地运行大模型的项目&#xff0c;今天再给大家介绍一个最厉害的开源大模型服务框架——ollama。 项目介绍 Oll…

移动硬盘无法读取?别慌!这些方法助你恢复数据!

在我们的日常工作和生活中&#xff0c;移动硬盘作为重要的数据存储工具&#xff0c;承载着珍贵资料。然而&#xff0c;移动硬盘无法被电脑读取的情况时有发生&#xff0c;令人焦急。别慌&#xff0c;下面为大家详细介绍恢复移动硬盘数据的有效方法。 一、检查硬件连接和驱动问题…

gevent- monkey 补丁

协程gevent模块的使用_gevent.spawn-CSDN博客 阅读上面的博客&#xff0c;就可以理解 gevent.monkey 的作用&#xff0c;就是自动将socket、time等标准库替换成异步的库&#xff0c;是gevent让代码从同步变成异步的一种方式 现在来一段上面博客的代码 import gevent# 函数1 …

基于华为云服务器的网页部署

这仅仅是对自己使用过程中部分记录。后续有时间我会把从服务器租用到网页部署的全过程都整理下。 1. 华为云服务器的租用 可以前往华为云租弹性云服务器&#xff0c;具体配置可以看自己的需求&#xff0c;链接在这&#xff0c;我这边选择了ubuntu18.04版本的系统。   租用成…

春之学习体验:SpringBoot教育平台开发

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于在线视频教育平台当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了在线视频教育平台&#xff0c;它彻底改变了过…

bestphp‘s revenge1

进入这个页面又是令人激动的代码审计环节. 不过再次之前呢先补充一些弥足珍贵的知识点. 前置知识点&#xff1a; call_user_func() call_user_func() 是 PHP 中的一个非常有用的函数&#xff0c;它允许你调用一个回调函数。回调函数可以是一个匿名函数&#xff08;也称为闭…

C#实现串口中继

前一段时间为了测试硬件产品&#xff0c;发现&#xff0c;串口转发不太方便&#xff0c;于是自己写了一个简单的串口中继小程序&#xff0c;是基于C#开发的&#xff0c;可以将两个串口互通&#xff0c;方便自己在程序的使用&#xff0c;目前看起来应用还可以&#xff0c;现在把…

2024年最新软件测试面试题【附文档答案】

【纯干货&#xff01;&#xff01;&#xff01;】花费了整整3天&#xff0c;整理出来的全网最实用软件测试面试大全&#xff0c;一共30道题目答案的纯干货&#xff0c;希望大家多多支持&#xff0c;建议 点赞&#xff01;&#xff01;收藏&#xff01;&#xff01;长文警告&…

《Discriminative Class Tokens for Text-to-Image Diffusion Models》ICCV2023

摘要 论文讨论了文本到图像扩散模型的最新进展&#xff0c;这些模型能够生成多样化和高质量的图像。然而&#xff0c;生成的图像常常缺乏细节&#xff0c;并且由于输入文本的歧义性&#xff0c;容易产生错误。为了解决这些问题&#xff0c;作者提出了一种非侵入式的微调技术&a…

AgentOhana:为智能体学习设计统一的数据和训练流水线

人工智能咨询培训老师叶梓 转载标明出处 多源数据异构性问题通常来源于多轮交互的Agent相关数据。不同数据集之间的数据结构、语法、标签约定和处理方法的多样性&#xff0c;使得LLM的训练和微调过程变得复杂&#xff0c;且容易引入偏差和不一致性。为了应对这些挑战&#xff…

2024年做了TMMi认证的中国企业有哪些

&#xff08;本文章企业名单来源&#xff1a;TMMi基金会官方网站&#xff09; 您是否想要了解2024年&#xff08;截至8月底&#xff09;有哪些新增企业做了TMMi认证&#xff08;不含2级&#xff09;&#xff1f; 以下是TMMi基金会官网查询的内容&#xff1a; TMMi基金会官网公…

数据结构————单链表

目录 一、单链表的定义及其特点 定义 特点 二、单链表的实现 准备工作&#xff1a; 1.单链表的创建 1.1头插法介绍 1.2尾插法介绍 总结&#xff1a; 2.单链表的初始化 3.单链表的求表长 4.单链表的销毁 5.单链表的插入 6.单链表的查找 6.1按序查找 6.2按值查找 7…

UDP聊天室项目

代码思路 服务器 #include <stdio.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h>…