线程知识点总结

news2024/11/15 11:12:00

Java线程是Java并发编程中的核心概念之一,它允许程序同时执行多个任务。以下是关于Java线程的一些关键知识点总结:

1. 线程的创建与启动

  • 继承Thread类:创建一个新的类继承Thread类,并重写其run()方法。通过创建该类的实例并调用start()方法来启动线程。

  • 实现Runnable接口:创建一个类实现Runnable接口,并实现run()方法。然后将该类的实例作为参数传递给Thread类的构造器,之后通过Thread对象调用start()方法。

  • 使用Executor框架(推荐):从Java 5开始引入,提供了一个更强大的线程管理机制,如Executors类可以创建固定大小的线程池、单线程执行器等,提高了线程复用和管理的效率。

在Java中,线程的创建和启动主要通过以下方式实现:

1. 继承Thread类

步骤如下:

  1. 定义一个新类继承自Thread
  2. 在新类中重写Thread类的run()方法。在这个方法里定义需要并行执行的代码逻辑。
  3. 创建新类的实例。
  4. 调用实例的start()方法来启动线程。注意,不要直接调用run()方法,因为那样会把run()当作普通方法在当前线程中执行,而不是启动新线程。

示例代码;

class MyThread extends Thread {
    public void run() {
        System.out.println("通过继承Thread类创建线程");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start(); // 启动线程
    }
}
2. 实现Runnable接口

步骤如下:

  1. 定义一个类实现Runnable接口
  2. 实现Runnable接口中的run()方法,放入线程需要执行的代码。
  3. 创建实现了Runnable接口的类的实例。
  4. 创建Thread类的实例,并将实现了Runnable接口的类的实例作为参数传递给Thread的构造函数。
  5. 调用Thread实例的start()方法来启动线程。

示例代码:

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("通过实现Runnable接口创建线程");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start(); // 启动线程
    }
}
3使用Executor框架(推荐)

从Java 5开始,还可以使用Executor框架来管理和控制线程,比如使用Executors类创建线程池,这种方式更加灵活且易于管理线程生命周期和资源。

示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            System.out.println("通过Executor框架创建线程");
        });
        executor.shutdown(); // 关闭线程池
    }
}

这三种方式中,使用Runnable接口和Executor框架更为灵活和推荐,因为它们提供了更好的解耦和线程池管理能力。

2. 线程状态

Java线程有以下几种状态:

Java线程在其生命周期中会经历多种状态,这些状态根据Java线程API定义,主要包括以下几种:

  • 新建(New): 线程刚被创建,尚未启动。当通过new关键字创建一个Thread对象时,线程处于此状态。

  • 可运行(Runnable): 线程可以被JVM调度执行。这个状态可以细分为两个子状态:

     (1)就绪(Ready): 线程已经具备运行条件,正在等待CPU分配时间片以便执行。     (2)运行中(Running): 线程获得CPU时间片,正在执行线程的run()方法。
  • 阻塞(Blocked): 线程因为某种原因(如等待锁、I/O操作等)而暂停执行,此时线程不会被分配CPU时间片,直到导致阻塞的原因解除。

  • 等待(Waiting): 线程因为调用了Object.wait()Thread.join()或者LockSupport.park()等方法而进入等待状态。这种状态下,线程必须等待其他线程执行特定动作(如通知notify()notifyAll())才能继续执行。

  • 超时等待(Timed Waiting): 与等待状态相似,但是有一个明确的等待时间限制,例如通过Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)等方法设置了等待时间。超过这个时间后,线程会自动恢复到可运行状态,无需其他线程显式唤醒。

  • 终止(Terminated): 线程执行完毕或因异常结束,线程生命周期结束。

3. 线程同步

为了防止多线程环境下的数据不一致问题,Java提供了以下同步机制:

  • synchronized关键字:用于方法或代码块,保证同一时刻只有一个线程可以访问被synchronized保护的代码或方法。
  • Lock接口(java.util.concurrent.locks):比synchronized更灵活,提供了更多的锁操作,如公平锁、非公平锁、可重入锁等。
  • volatile关键字:用于变量,确保了多线程之间的可见性,但不保证原子性。

Java线程同步是为了避免多线程环境下对共享资源的不正确访问而导致的数据不一致性问题。Java提供了多种线程同步机制来保障线程安全,主要包含以下几种:

1. synchronized关键字
  • 方法同步:在方法声明上使用synchronized关键字,这样一次只能有一个线程访问该方法。
public synchronized void synchronizedMethod() {
    // 方法体
}
  • 代码块同步:可以在特定的代码块上使用synchronized,指定一个对象作为锁。
public void synchronizedBlockMethod() {
    synchronized(this) {
        // 需要同步的代码块
    }
}
2. Lock接口

java.util.concurrent.locks.Lock接口提供了比synchronized更灵活的锁定机制,它允许尝试非阻塞地获取锁、能够被中断地等待锁以及超时获取锁等特性。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class X {
    private final Lock lock = new ReentrantLock();

    public void method() {
        lock.lock();
        try {
            // 需要同步的代码
        } finally {
            lock.unlock();
        }
    }
}
3. volatile关键字

虽然volatile不是一种同步机制,但它能保证变量的可见性和部分有序性,适用于状态标记量的读写操作。

private volatile boolean flag = false;

public void setFlag(boolean newValue) {
    flag = newValue;
}

public boolean getFlag() {
    return flag;
}
4. 原子类(Atomic)

Java提供了java.util.concurrent.atomic包下的原子类,如AtomicIntegerAtomicBoolean等,它们通过CAS(Compare and Swap,比较并交换)操作实现线程安全的更新操作。

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}
5. Condition接口

Condition接口(在Lock接口的实现类中提供)用于更复杂的线程间协调,它是对传统Object类中的wait()notify()notifyAll()方法的改进版本,可以和Lock一起使用,实现更细粒度的线程同步。

总结

选择合适的线程同步机制取决于具体的应用场景。对于简单的同步需求,synchronized关键字往往是首选;对于更复杂的并发控制,LockCondition提供了更高的灵活性;而对于基本类型的原子操作,原子类提供了简单且高效的解决方案。正确应用这些同步机制,可以有效地避免竞态条件,保证程序的线程安全。

4. 线程间通信

  • wait()、notify()、notifyAll():这些方法定义在Object类中,用于线程间的等待/通知机制,必须在同步上下文中使用。
  • Condition接口(java.util.concurrent.locks):作为Lock接口的一部分,提供了更灵活的线程间协调行为,可以替代传统的wait/notify。

Java中线程间的通信主要是指一个线程向另一个线程发送信号,或者多个线程之间互相协作完成任务的能力。Java提供了多种机制来支持线程间的通信,以下是一些常用的方法:

1. wait(), notify(), notifyAll()

这三个方法都是在Object类中定义的,常用于线程间的同步和通信。它们必须在同步代码块或同步方法中使用。

  • wait():使当前线程等待,并释放对象的监视器锁。等待直到其他线程调用该对象的notify()notifyAll()方法。
  • notify():唤醒在此对象监视器上等待的单个线程,选择是任意的,并且在Java中不可预测。
  • notifyAll():唤醒在此对象监视器上等待的所有线程。
2. Condition接口

java.util.concurrent.locks.Condition接口提供了类似wait()notify()notifyAll()的功能,但功能更强大,它可以和Lock配合使用,实现更灵活的线程间协调行为。

  • await():类似wait(),使当前线程等待,并释放锁。
  • signal():唤醒一个等待的线程,类似notify()
  • signalAll():唤醒所有等待的线程,类似notifyAll()
3. CountDownLatch

java.util.concurrent.CountDownLatch是一个同步辅助类,它允许一个或多个线程等待其他线程完成一系列操作后再继续执行。

CountDownLatch latch = new CountDownLatch(n);
// n个线程完成任务后调用latch.countDown();
latch.await(); // 其他线程在此等待,直到计数器为0
4. CyclicBarrier

java.util.concurrent.CyclicBarrier也是同步辅助类,它允许一组线程相互等待,直到到达某个公共屏障点后再一起继续执行。

CyclicBarrier barrier = new CyclicBarrier(parties);
// 线程执行到barrier点时调用await()
barrier.await();
5. Semaphore

java.util.concurrent.Semaphore是一个计数信号量,可以控制同时访问特定资源的线程数量。

Semaphore semaphore = new Semaphore(permits);
semaphore.acquire(); // 获取许可
// 执行代码
semaphore.release(); // 释放许可
6. Exchanger

java.util.concurrent.Exchanger用于在线程间进行数据交换,两个线程通过exchange()方法交换数据,只有双方都准备好时才会发生交换。

Exchanger<String> exchanger = new Exchanger<>();
// 线程A
String dataA = "From A";
String received = exchanger.exchange(dataA); // 等待与B交换数据
// 线程B
String dataB = "From B";
received = exchanger.exchange(dataB); // 等待与A交换数据

正确使用这些线程间通信机制,可以有效地帮助开发人员解决多线程环境下的同步和协作问题。

5. 线程池

  • Executor框架:提供了一组线程池相关的类,如ThreadPoolExecutorScheduledThreadPoolExecutor等,用于管理和控制线程的创建、执行和销毁,提高性能和资源利用率。

Java线程池是Java并发编程中的一个重要概念,它是一种基于池化概念管理线程的技术,可以重复使用预先创建的线程,以减少线程创建和销毁的开销,提高响应速度和整体性能。Java通过java.util.concurrent.Executor框架来支持线程池的创建和管理,其中最常用的接口和类包括:

1. Executor接口

这是最顶层的执行者接口,它定义了一个execute(Runnable command)方法来执行给定的任务。

2. ExecutorService接口

扩展了Executor接口,提供了更丰富的管理任务和线程池的方法,如提交Callable任务、关闭线程池等。

3. ThreadPoolExecutor类

实现了ExecutorService接口,是最常见的线程池实现类,提供了高度可配置的线程池实现。

4. ScheduledExecutorService接口

扩展了ExecutorService接口,支持计划执行任务,即定时或周期性任务。

5. ScheduledThreadPoolExecutor类

实现了ScheduledExecutorService接口,用于支持定时及周期性任务的线程池。

6. Executors工厂类

提供了一系列静态方法来创建不同类型的线程池,包括:

  • newFixedThreadPool(int nThreads): 创建一个固定大小的线程池,可重用固定数量的线程,适合执行大量短期异步任务。
  • newSingleThreadExecutor(): 创建一个只有一个线程的线程池,确保所有的任务按照顺序执行。
  • newCachedThreadPool(): 创建一个可缓存线程池,如果线程池长度超过处理所需,可灵活回收空闲线程,若无可回收,则新建线程。
  • newScheduledThreadPool(int corePoolSize): 创建一个固定核心线程数的线程池,支持定时及周期性任务执行。
  • newWorkStealingPool(int parallelism): (Java 8引入)创建一个拥有多个任务队列的线程池,使用工作窃取算法来充分利用CPU资源。
使用示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker); // 提交任务到线程池执行
        }
        
        // 关闭线程池,不再接受新的任务,已提交的任务会执行完
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("所有任务已完成");
    }
}

class WorkerThread implements Runnable {
    private String command;

    public WorkerThread(String s) {
        this.command = s;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 开始. 命令 = " + command);
        processCommand();
        System.out.println(Thread.currentThread().getName() + " 结束.");
    }

    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

合理使用线程池可以有效地管理线程生命周期、提高系统资源的使用效率,以及提升系统的响应速度和吞吐量。但同时也需要注意,不当的配置和使用可能会导致资源耗尽、死锁等问题,因此需要仔细设计和监控线程池的使用情况。

6. 并发工具类

Java并发包(java.util.concurrent)还提供了许多实用的并发工具类,如:

  • CountDownLatch:允许一个或多个线程等待其他线程完成一系列操作。
  • CyclicBarrier:让一组线程等待所有线程到达某个屏障后再一起执行后续操作。
  • Semaphore:控制同时访问特定资源的线程数量。
  • FutureCallable:用于异步计算,可以获取线程执行的结果。

Java并发编程库(java.util.concurrent包及其子包)提供了丰富的工具类来帮助开发者高效、安全地处理并发问题。以下是一些常用的并发工具类:

1. CountDownLatch

java.util.concurrent.CountDownLatch是一个同步辅助类,允许一个或多个线程等待其他线程完成一系列操作。初始化时设置一个计数器,每当一个任务完成就递减计数器,计数器为0时,所有等待的线程被释放。

2. CyclicBarrier

java.util.concurrent.CyclicBarrier也是一种同步辅助类,它允许一组线程相互等待,直到达到一个共同的屏障点,然后所有线程一起继续执行。与CountDownLatch不同的是,它可以在重置后重复使用。

3. Semaphore

java.util.concurrent.Semaphore是一个计数信号量,可以用来控制同时访问特定资源的线程数量,或者控制同时执行的任务数量。

4. Exchanger

java.util.concurrent.Exchanger用于两个线程之间交换对象,当两个线程都到达同步点并且准备好交换时,Exchanger会交换这两个线程的对象。

5. Phaser

java.util.concurrent.Phaser是一个可重用的同步栏栅,它支持注册多个 parties,并在每个阶段(phase)等待所有 parties 到达屏障点。

6. ConcurrentCollections

Java并发包提供了线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayListConcurrentSkipListMap等,这些集合类在高并发环境下表现更好。

7. Locks
  • ReentrantLock: 可重入互斥锁,提供比synchronized更灵活的锁定机制,如公平锁、非公平锁、尝试获取锁等。
  • ReentrantReadWriteLock: 支持读写分离的锁,允许多个读线程同时访问,但只允许一个写线程或一个读线程和一个写线程同时访问。
8. Executors

java.util.concurrent.Executors工厂类提供了创建不同类型线程池的方法,如newFixedThreadPoolnewSingleThreadExecutornewCachedThreadPool等。

9. Future & Callable
  • Future: 代表异步计算的结果,提供了检查计算是否完成、获取结果、取消计算等方法。
  • Callable: 类似于Runnable,但可以返回结果,并且可以抛出异常,通常与FutureTask和线程池一起使用。
10. ForkJoinPool 和 RecursiveTask / RecursiveAction

Fork/Join框架,用于并行处理大数据集,通过将大任务拆分成小任务并行处理。RecursiveTask用于有返回值的任务,RecursiveAction用于没有返回值的任务。

这些工具类覆盖了并发编程中的大多数场景,合理运用它们可以大大提高并发程序的性能和可靠性。

7. 死锁与避免

死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力干涉,它们都将无法推进下去。避免死锁的方法包括:

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock的tryLock方法代替内部锁机制。
  • 按顺序加锁,确保所有线程按照相同的顺序请求锁。

在Java中,死锁是指两个或多个线程互相等待对方持有的锁,从而导致所有线程都无法继续执行的情况。

四个必要条件:
  1. 互斥条件:资源不能被多个线程同时占有,至少有一个资源必须是独占的。
  2. 请求与保持条件:已经持有至少一个资源的线程尝试获取额外的资源。
  3. 不可抢占条件:已分配给线程的资源在该线程释放前不能被其他线程抢占。
  4. 循环等待条件:存在一种线程资源的循环等待链,每个线程都持有下一个线程所需的资源,并等待先前线程释放资源。
避免死锁:
  1. 避免嵌套锁:尽量减少在一个线程中同时获取多个锁的需求。如果必须获取多个锁,确保所有线程以相同的顺序获取锁。
  2. 使用超时锁:使用tryLock(long time, TimeUnit unit)方法尝试获取锁,如果在指定时间内无法获取到锁,则放弃并处理相应的逻辑,而不是无限等待。
  3. 锁顺序:总是按照固定的顺序获取锁,这样可以避免循环等待的条件,例如定义全局的锁顺序或者使用锁的自然顺序(如锁对象的hashcode排序)。
  4. 锁分解:将大范围的锁分解为多个小范围的锁,减少锁的竞争。
  5. 使用并发工具类:利用java.util.concurrent包提供的高级并发工具,如SemaphoreCountDownLatchCyclicBarrierExchanger等,这些工具设计时已经考虑了线程安全和死锁问题。
  6. 按需加锁:尽量减少锁的使用,只有在真正需要同步资源时才加锁,不必要的锁会导致不必要的阻塞和潜在的死锁风险。
  7. 检测与恢复:虽然Java标准库本身不直接提供死锁检测工具,但在复杂系统中可以设计监控和诊断机制,定期检查线程状态,一旦检测到死锁,可以采取重启线程或事务等恢复措施。

通过上述策略,可以大大降低程序中死锁发生的概率,提高系统的稳定性和响应性。

掌握以上知识点有助于深入理解Java线程机制,并在实际开发中有效利用多线程提升程序性能。

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

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

相关文章

【C语言】插入排序(经典算法,建议收藏!!!)

目录 1、原理2、代码展示3、解析代码4、适用场景 1、原理 插入排序&#xff08;Insertion Sort&#xff09;是一种简单直观的排序算法&#xff0c;其原理可以简述如下&#xff1a; 1.分已排序区间和未排序区间: 将数组分为已排序区间和未排序区间。初始时&#xff0c;已排序区…

Vue3父组件如何访问子组件属性和方法

本篇内容主要是父组件如何访问子组件的属性和方法 文章目录 子组件 //son.vue代码const list (info) >{console.log(info) }const name ref("XXXX")//子组件向父组件暴露了一个方法&#xff0c;然后父组件就可以去使用子组件里面的一些属性和方法了 //子组件向…

突破网络屏障:掌握FRP内网穿透技术

1.FRP介绍 1.frp是什么 frp 是一款高性能的反向代理应用&#xff0c;专注于内网穿透。它支持多种协议&#xff0c;包括 TCP、UDP、HTTP、HTTPS 等&#xff0c;并且具备 P2P 通信功能。使用 frp&#xff0c;您可以安全、便捷地将内网服务暴露到公网&#xff0c;通过拥有公网 I…

Python 潮流周刊#55:分享 9 个高质量的技术类信息源!

大家好&#xff0c;我是猫哥&#xff0c;今天给大家分享几个高质量的技术类信息源。 本文分享的信息源都是周刊类型的&#xff0c;所谓周刊类&#xff0c;就是以固定每周的频率更新&#xff0c;每期分享很多精华内容的链接。它的特点是信息密度极高&#xff0c;可以节省你去查…

log4j日志打印导致OOM问题

一、背景 某天压测&#xff0c;QPS压到一定值后机器就开始重启&#xff0c;出现OOM&#xff0c;好在线上机器配置了启动参数-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/**/**heapdump.hprof。将dump文件下载到本地&#xff0c;打开Java sdk bin目录下的jvisualvm工具&a…

2024PTA算法竞赛考试编程题代码

目录 前言 题目和代码 L1-006 连续因子 L1-009 N个数求和 L2-004 这是二叉搜索树吗&#xff1f; L2-006 树的遍历 L2-007 家庭房产 L4-118 均是素数 L4-203 三足鼎立 L2-002 链表去重 L2-003 月饼 L2-026 小字辈 L4-201 出栈序列的合法性 L4-205 浪漫侧影 前言 所…

BarTender 常见的使用要点

BarTender 简述 BarTender是由美国海鸥科技&#xff08;Seagull Scientific&#xff09;推出的一款条码打印软件&#xff0c;被广泛应用于标签、条形码、证卡和RFID标记的设计和打印领域。它在全球范围内拥有众多用户&#xff0c;被公认为标签打印方面的全球领先者。BarTender…

把 FolkMQ 内嵌到 SpringBoot2 项目里(比如 “诺依” 啊)

FolkMQ &#xff08;消息中间件&#xff09;支持内嵌、单机、集群、多重集群等多种部署方式。 内嵌版&#xff0c;就相当于 H2 或 SQLite 数据库一样。给一些小项目&#xff08;或者特别需求&#xff09;带来了方便。大项目&#xff0c;则可以使用独立部署的 “单机版” 或 “…

搞懂银行的各类号码 — Account Number, Routing Number 和 Swift Code

1. 前言2. 名词解释 2.1. Debit Card Number 储蓄卡卡号2.2. Account Number 账户号码2.3. Routing Number 路由号码2.4. SWIFT Code SWIFT 号码3. 查找信息 3.1. 支票3.2. 网上银行3.3. 手机银行4. SWFIT Code 4.1. 看懂 SWIFT Code4.2. 询问银行4.3. Google 大神4.4. 部分常用…

24.6.9( 概率dp)

星期一&#xff1a; abc356 D atc传送门 思路&#xff1a;按位与操作&#xff0c;M的非零位对答案一定没有贡献&#xff0c;对M为1的位&#xff0c;考虑有多少k此位也为1 按位枚举&#xff0c;m此位为0跳…

CAS Server Restful接口实现后台认证

背景 对于一些比较复杂定制化登录页的情况下&#xff0c;之前提到过可以自定义修改使用CAS Server提供的登录页这种操作已经明显跟不上复杂定制场景了&#xff0c;所以CAS Server也提供了支持Restful接口&#xff0c;支持服务端后台登陆&#xff0c;对于复杂登陆场景时&#x…

vscode 访问容器的方式

方法一&#xff1a;先连服务器&#xff0c;再转入容器 配置客户机A M1. 客户机A通过 vscode 连接服务器B&#xff0c;再连接容器C 配置vscode的ssh配置文件&#xff1a;~.ssh\config&#xff08;当需要多个不同的连接时&#xff0c;使用 IdentityFile 指定公钥位置&#xff09;…

20240605解决飞凌的OK3588-C的核心板刷机原厂buildroot不能连接ADB的问题

20240605解决飞凌的OK3588-C的核心板刷机原厂buildroot不能连接ADB的问题 2024/6/5 13:53 rootrootrootroot-ThinkBook-16-G5-IRH:~/repo_RK3588_Buildroot20240508$ ./build.sh --help rootrootrootroot-ThinkBook-16-G5-IRH:~/repo_RK3588_Buildroot20240508$ ./build.sh lun…

280 基于matlab的摇号系统GUI界面仿真MATLAB程序

基于matlab的摇号系统GUI界面仿真MATLAB程序&#xff0c;输入总数量及摇号需求&#xff0c;进行随机性摇号&#xff0c;并对摇取的号码进行双重随机性数据检测&#xff0c;确定是否符合要求。程序已调通&#xff0c;可直接运行。 280 GUI人机交互 摇号系统GUI界面仿真 - 小红书…

本地搭建支持语音和文本的中英文翻译服务-含全部源代码

实现目标 1、支持文本中英文互译&#xff1b; 2、支持中文语音输入&#xff1b; 3、支持英文语言输入&#xff1b; 进阶&#xff08;未实现&#xff09; 4、优化web界面&#xff1b; 5、优化语音输入js实现逻辑&#xff1b; 6、增加语音输入自纠错模型&#xff0c;纠正语音识别…

【PR2019】怎样批量添加转场效果及修改默认持续时间

一&#xff0c;设置“交叉溶解”效果到所有素材 选择效果&#xff0c;右击“将所选过渡设置为默认过渡”&#xff1a; 框选所有素材&#xff0c;“Ctrl D”&#xff1a; 每个素材中间有有了交叉溶解的效果&#xff1a; 二&#xff0c;修改效果属性 2.1&#xff0c;单个修…

从零开始,手把手教你文旅产业策划全攻略

如果你想深入了解文旅策划的世界&#xff0c;那么有很多途径可以获取知识和灵感。 首先&#xff0c;阅读一些专业书籍也是一个不错的选择。书店或图书馆里有许多关于文旅策划的书籍&#xff0c;它们通常涵盖了策划的基本理论、方法和实践案例。通过阅读这些书籍&#xff0c;你…

激光点云配准算法——Cofinet / GeoTransforme / MAC

激光点云配准算法——Cofinet / GeoTransformer / MAC GeoTransformer MAC是当前最SOTA的点云匹配算法&#xff0c;在之前我用总结过视觉特征匹配的相关算法 视觉SLAM总结——SuperPoint / SuperGlue 本篇博客对Cofinet、GeoTransformer、MAC三篇论文进行简单总结 1. Cofine…

热题系列章节5

169. 多数元素 给定一个大小为 n 的数组&#xff0c;找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1: 输入: [3,2,3] 输出: 3 示例 2: 输入: [2,2,1,1,1,2,2] 输出:…

【C语言】11.字符函数和字符串函数

文章目录 1.字符分类函数2.字符转换函数3.strlen的使用和模拟实现4.strcpy的使用和模拟实现5.strcat的使用和模拟实现6.strcmp的使用和模拟实现7.strncpy函数的使用8.strncat函数的使用9.strncmp函数的使用10.strstr的使用和模拟实现11.strtok函数的使用12.strerror函数的使用 …