Java多线程深入探讨
- 1. 线程与进程
- 2. 创建和管理线程
- 2.1. 继承Thread类
- 2.2. 实现Runnable接口
- 2.3 利用Callable、FutureTask接口实现。
- 2.4 Thread的常用方法
- 3. 线程同步
- 3.1. synchronized关键字
- 3.1.1同步代码块:
- 3.1.2 同步方法:
- 3.2. Lock接口
- 4. 线程间通信
- 5. 线程池
- 5.1 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
- 5.2 使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
- 6. Java并发工具类
- 6.1. CountDownLatch
- 6.2. CyclicBarrier
- 6.3. Semaphore
- 6.4. ConcurrentHashMap
- 6.5. BlockingQueue
- 7. 总结
- 9. 线程优先级
- 10. 守护线程
- 11. 线程局部变量
- 12. 线程异常处理
- 13. 实践建议
Java多线程编程是Java程序员必备的技能之一,因为它可以帮助我们编写更高效、更快速的程序。在本文中,我们将深入探讨Java多线程的基本概念、线程的创建和管理、同步、线程池以及Java并发工具类。
1. 线程与进程
进程是操作系统进行资源分配的基本单位,它是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,它是系统进行资源分配和调度的一个独立单位。
线程是进程中的一个实体,它是被操作系统独立调度和执行的基本单位。一个进程中可以有多个线程,这些线程共享进程的资源,如内存、文件句柄等。线程之间的切换比进程切换要快得多,因为线程间通信成本更低。
2. 创建和管理线程
在Java中,有两种方法可以创建线程:(还有JDK5新增了一种方法:实现Callable接口)
2.1. 继承Thread类
步骤:
①定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
②创建MyThread类的对象
③调用线程对象的start()方法启动线程(启动后还是执行run方法的)
创建一个新类,该类继承自Thread
类,然后覆盖run
方法:
class MyThread extends Thread {@Overridepublic void run() {// 你的线程代码}
}// 在主程序中创建并启动线程:
MyThread myThread = new MyThread();
myThread.start();
优缺点:
- 优点:编码简单
- 缺点:存在单继承的局限性,线程类继承Thread后,不能继承其他类,不便于扩展
注意:
1、为什么不直接调用了run方法,而是调用start启动线程。
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行。
- 只有调用start方法才是启动一个新的线程执行。
2、把主线程任务放在子线程之前了。
-
这样主线程一直是先跑完的,相当于是一个单线程的效果了。
2.2. 实现Runnable接口
步骤:
①定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
②创建MyRunnable任务对象
③把MyRunnable任务对象交给Thread处理。
④调用线程对象的start()方法启动线程
创建一个新类,实现Runnable
接口,然后实现run
方法:
class MyRunnable implements Runnable {@Overridepublic void run() {// 你的线程代码}
}// 在主程序中创建并启动线程:
Thread thread = new Thread(new MyRunnable());
thread.start();
实现Runnable
接口通常是更好的选择,因为它允许你的类继承其他类,而Java不支持多继承。
优缺点:
- 优点:线程任务类只是实现了Runnale接口,可以继续继承类和实现接口,扩展性强。
- 缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
Thread的构造器
构造器 | 说明 |
---|---|
public Thread(String name) | 可以为当前线程指定名称 |
public Thread(Runnable target) | 封装Runnable对象成为线程对象 |
public Thread(Runnable target ,String name ) | 封装Runnable对象成为线程对象,并指定线程名称 |
2.3 利用Callable、FutureTask接口实现。
前言:
1、前2种线程创建方式都存在一个问题:
- 他们重写的run方法均不能直接返回结果。
- 不适合需要返回线程执行结果的业务场景。
2、怎么解决这个问题呢?
- JDK 5.0提供了Callable和FutureTask来实现。
- 这种方式的优点是:可以得到线程执行的结果。
步骤:
①、得到任务对象
1.定义类实现Callable接口,重写call方法,封装要做的事情。
2.用FutureTask把Callable对象封装成线程任务对象。
②、把线程任务对象交给Thread处理。
③、调用Thread的start方法启动线程,执行任务
④、线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果。
FutureTask的API
方法名称 | 说明 |
---|---|
public FutureTask<>(Callable call) | 把Callable对象封装成FutureTask对象。 |
public V get() throws Exception | 获取线程执行call方法返回的结果。 |
优缺点:
- 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
- 可以在线程执行完毕后去获取线程执行的结果。
- 缺点:编码复杂一点。
总结:
方式 | 优点 | 缺点 |
---|---|---|
继承Thread类 | 编程比较简单,可以直接使用Thread类中的方法 | 扩展性较差,不能再继承其他的类,不能返回线程执行的结果 |
实现Runnable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。 | 编程相对复杂,不能返回线程执行的结果 |
实现Callable接口 | 扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果 | 编程相对复杂 |
2.4 Thread的常用方法
Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread()。
- 当有很多线程在执行的时候,我们怎么去区分这些线程呢?
此时需要使用Thread的常用方法:getName()、setName()、currentThread()等。
Thread获取和设置线程名称
方法名称 | 说明 |
---|---|
String getName() | 获取当前线程的名称,默认线程名称是Thread-索引 |
void setName(String name) | 将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称 |
Thread类获得当前线程的对象
方法名称 | 说明 |
---|---|
public static Thread currentThread(): | 返回对当前正在执行的线程对象的引用 |
注意:
1、此方法是Thread类的静态方法,可以直接使用Thread类调用。 2、这个方法是在哪个线程执行中调用的,就会得到哪个线程对象。
Thread类的线程休眠方法
方法名称 | 说明 |
---|---|
public static void sleep(long time) | 让当前线程休眠指定的时间后再继续执行,单位为毫秒。 |
3. 线程同步
在多线程环境下,如果多个线程同时访问共享资源,可能会导致数据不一致的问题。为了解决这个问题,我们需要对共享资源进行同步。
线程同步的核心思想
- 加锁,把共享资源进行上锁,每次只能一个线程进入访问完毕以后解锁,然后其他线程才能进来。
3.1. synchronized关键字
在Java中,我们可以使用synchronized
关键字来实现同步。synchronized
可以修饰方法或者代码块。当一个线程进入synchronized
修饰的方法或代码块时,其他线程将被阻塞,直到该线程离开。
以下是一个使用synchronized
关键字的示例:
class Counter {private int count;public synchronized void increment() {count++;}public synchronized void decrement() {count--;}public synchronized int getCount() {return count;}
}
3.1.1同步代码块:
作用:把出现线程安全问题的核心代码给上锁。
原理:每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。
锁对象要求
- 理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可。
锁对象的规范要求
规范上:建议使用共享资源作为锁对象。
对于实例方法建议使用this作为锁对象。
对于静态方法建议使用字节码(类名.class)对象作为锁对象。
3.1.2 同步方法:
作用:把出现线程安全问题的核心方法给上锁。
原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
原理:
- 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
- 如果方法是实例方法:同步方法默认用this作为的锁对象。但是代码要高度面向对象!
- 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
3.2. Lock接口
除了使用synchronized
关键字外,Java还提供了java.util.concurrent.locks
包中的Lock
接口,该接口允许我们实现更灵活、更细粒度的同步。ReentrantLock
是Lock
接口的一个实现,它允许线程以可重入的方式获取锁。
以下是一个使用ReentrantLock
的示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Counter {private int count;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public void decrement() {lock.lock();try {count--;} finally {lock.unlock();}}public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}}
}
4. 线程间通信
线程间通信是指多个线程之间互相发送消息或数据的过程。在Java中,我们可以使用wait
、notify
和notifyAll
方法实现线程间通信。
线程通信常见形式
- 通过共享一个数据的方式实现。
- 根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做。
应用场景:
生产者与消费者模型:生产者线程负责生产数据,消费者线程负责消费生产者产生的数据。
要求:生产者线程生产完数据后唤醒消费者,然后等待自己,消费者消费完该数据后唤醒生产者,然后等待自己。
方法名称 | 说明 |
---|---|
void wait() | 让当前线程等待并释放所占锁,直到另一个线程调用notify()方法或 notifyAll()方法 |
void notify() | 唤醒正在等待的单个线程 |
void notifyAll() | 唤醒正在等待的所有线程 |
以下是一个使用wait
和notify
实现生产者消费者模型的示例:
import java.util.LinkedList;
import java.util.Queue;public class ProducerConsumerExample {public static void main(String[] args) {Queue<Integer> queue = new LinkedList<>();int maxSize = 10;Thread producer = new Thread(new Producer(queue, maxSize), "Producer");Thread consumer = new Thread(new Consumer(queue), "Consumer");producer.start();consumer.start();}
}class Producer implements Runnable {private Queue<Integer> queue;private int maxSize;public Producer(Queue<Integer> queue, int maxSize) {this.queue = queue;this.maxSize = maxSize;}@Overridepublic void run() {int value = 0;while (true) {synchronized (queue) {while (queue.size() == maxSize) {try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Producing " + value);queue.add(value++);queue.notifyAll();}}}
}class Consumer implements Runnable {private Queue<Integer> queue;public Consumer(Queue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {synchronized (queue) {while (queue.isEmpty()) {try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}int value = queue.poll();System.out.println("Consuming " + value);queue.notifyAll();}}}
}
5. 线程池
线程池是一种管理线程的机制,它允许我们重用线程以减少创建和销毁线程的开销。在Java中,我们可以使用ExecutorService
接口和Executors
类来创建和管理线程池。
5.1 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
ThreadPoolExecutor构造器的参数说明
注:
临时线程什么时候创建啊?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。
ExecutorService的常用方法
方法名称 | 说明 |
---|---|
void execute(Runnable command) | 执行任务/命令,没有返回值,一般用来执行 Runnable 任务 |
Future submit(Callable task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务 |
void shutdown() | 等任务执行完毕后关闭线程池 |
List<Runnable]> shutdownNow() | 立刻关闭,停止正在执行的任务,并返回队列中未执行的任务 |
5.2 使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
Executors得到线程池对象的常用方法
- Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
方法名称 | 说明 |
---|---|
public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了一段时间则会被回收掉。 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
public static ExecutorService newSingleThreadExecutor () | 创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |
以下是一个使用线程池的示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {Runnable worker = new WorkerThread("" + i);executorService.execute(worker);}executorService.shutdown();while (!executorService.isTerminated()) {}System.out.println("All threads finished");}
}class WorkerThread implements Runnable {private String message;public WorkerThread(String message) {this.message = message;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " processing message: " + message);processMessage();}private void processMessage() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
6. Java并发工具类
Java并发包java.util.concurrent
提供了许多并发工具类,它们可以帮助我们简化多线程编程。以下是一些常用的并发工具类:
6.1. CountDownLatch
CountDownLatch
允许一个或多个线程等待其他线程完成操作。它包含一个初始计数值,任何调用await
方法的线程都将被阻塞,直到计数值为0。
6.2. CyclicBarrier
CyclicBarrier
允许一组线程相互等待,直到所有线程都到达某个屏障点。当最后一个线程到达屏障点时,所有等待的线程将被释放。
6.3. Semaphore
Semaphore
用于限制可以访问某个资源的线程数量。当一个线程要访问受限资源时,需要先获得信号量;当线程完成操作时,释放信号量。如果没有可用的信号量,线程将阻塞,直到有线程释放信号量。
6.4. ConcurrentHashMap
ConcurrentHashMap
是一个线程安全的HashMap实现,它允许多个线程并发访问。与使用synchronized
关键字的HashMap相比,它具有更好的性能。
6.5. BlockingQueue
BlockingQueue
是一个阻塞队列,它支持线程安全的插入和删除操作。当队列为空时,删除操作将阻塞;当队列满时,插入操作将阻塞。BlockingQueue
在生产者-消费者模型中非常有用。
7. 总结
本文深入探讨了Java多线程编程的基本概念、线程的创建和管理、同步、线程池以及Java并发工具类。了解这些概念和技术对于编写高效、高性能的Java程序至关重要。希望本文能为你的Java多线程编程之旅提供有用的指导!## 8. 线程的状态和生命周期
在Java中,线程具有以下状态:
- 新建(New):当我们创建了一个线程对象,但还没有调用
start()
方法时,线程处于新建状态。 - 就绪(Runnable):当线程对象调用了
start()
方法后,线程进入就绪状态。在这个状态下,线程已经准备好运行,等待操作系统分配时间片。 - 运行(Running):线程获得时间片并开始执行
run()
方法中的代码时,线程处于运行状态。 - 阻塞(Blocked):线程在等待获取锁以进入同步代码块或方法时,线程进入阻塞状态。
- 等待(Waiting):线程在等待其他线程执行特定操作(如调用
notify()
或notifyAll()
方法)时,线程进入等待状态。例如,线程调用了Object.wait()
、Thread.join()
或LockSupport.park()
方法。 - 超时等待(Timed Waiting):线程在等待其他线程执行特定操作,但最多等待指定的时间。例如,线程调用了
Thread.sleep()
、Object.wait(long timeout)
或LockSupport.parkNanos()
方法。 - 终止(Terminated):线程完成了
run()
方法的执行或因异常而终止,线程进入终止状态。
要获取线程的当前状态,可以调用Thread.getState()
方法。
9. 线程优先级
在Java中,线程具有优先级。优先级较高的线程可能会比优先级较低的线程获得更多的执行时间。线程优先级是通过Thread.setPriority(int)
方法设置的,取值范围为1到10,其中1表示最低优先级,10表示最高优先级。可以使用Thread.getPriority()
方法获取线程的当前优先级。
需要注意的是,线程优先级并不能保证线程执行的顺序,它只是影响线程获得执行时间的可能性。因此,在编写多线程程序时,不应依赖线程优先级来实现正确的同步或顺序。
10. 守护线程
守护线程(Daemon Thread)是一种特殊类型的线程,主要用于在后台为其他线程提供服务。当所有非守护线程结束时,守护线程会自动结束。垃圾回收线程就是一个典型的守护线程。
要将线程设置为守护线程,可以在调用start()
方法之前调用Thread.setDaemon(true)
方法。可以使用Thread.isDaemon()
方法检查线程是否为守护线程。
注意:不要将可能与其他线程进行交互的线程设置为守护线程,因为守护线程可能会在任何时候终止。如果守护线程在与其他线程交互时突然终止,可能会导致不可预测的行为或数据不一致。
11. 线程局部变量
线程局部变量(Thread-Local Variables)是一种特殊类型的变量,它为每个线程提供独立的变量副本。这意味着每个线程可以访问和修改它自己的变量副本,而不会影响其他线程的副本。
Java中提供了ThreadLocal
类来实现线程局部变量。要创建一个线程局部变量,可以创建一个ThreadLocal
对象,并调用get()
和set(T value)
方法来获取和设置线程的变量副本。以下是一个使用ThreadLocal
的示例:
public class ThreadLocalExample {private static ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {int counter = threadLocalCounter.get();threadLocalCounter.set(counter + 1);System.out.println("Thread-1: " + threadLocalCounter.get());}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 5; i++) {int counter = threadLocalCounter.get();threadLocalCounter.set(counter + 1);System.out.println("Thread-2: " + threadLocalCounter.get());}}});t1.start();t2.start();}
}
在这个示例中,threadLocalCounter
是一个线程局部变量。我们为每个线程提供了初始值0。当每个线程执行时,它们都会访问和修改自己的计数器副本,而不会影响其他线程的副本。
12. 线程异常处理
当线程抛出未捕获的异常时,Java提供了一种机制来处理这些异常。可以通过实现Thread.UncaughtExceptionHandler
接口并调用Thread.setDefaultUncaughtExceptionHandler()
方法来设置全局的未捕获异常处理器。此外,还可以通过调用Thread.setUncaughtExceptionHandler()
方法为单个线程设置未捕获异常处理器。
以下是一个设置未捕获异常处理器的示例:
public class ThreadExceptionHandlingExample {public static void main(String[] args) {Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println(t.getName() + " throws exception: " + e);}});Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(1 / 0);}}, "Thread-1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {throw new RuntimeException("An exception occurred in Thread-2");}}, "Thread-2");t1.start();t2.start();}
}
在这个示例中,我们设置了一个全局的未捕获异常处理器,用于处理所有线程抛出的未捕获异常。当Thread-1
和Thread-2
抛出未捕获的异常时,异常处理器会捕获并处理这些异常。
13. 实践建议
在进行多线程编程时,以下是一些建议和注意事项:
- 尽量使用
java.util.concurrent
包中提供的高级并发工具类,而不是直接使用Thread
和synchronized
关键字。这可以简化代码并提高程序的可维护性和性能。 - 在编写多线程程序时,避免使用全局变量或共享状态,以降低同步的复杂性和可能出现的错误。
- 要充分利用Java的内存模型,确保线程之间正确地共享和同步数据。
- 使用线程池来管理和调度线程,以提高程序性能。
- 避免线程优先级来实现正确的同步或顺序,因为它不能保证线程执行的顺序。
- 使用守护线程为其他线程提供后台服务,但不要将可能与其他线程进行交互的线程设置为守护线程。
- 当需要为每个线程提供独立的变量副本时,使用线程局部变量。
- 为线程设置未捕获异常处理器,以便在线程抛出未捕获的异常时进行处理。
相关文章:
Java多线程深入探讨
1. 线程与进程2. 创建和管理线程2.1. 继承Thread类2.2. 实现Runnable接口2.3 利用Callable、FutureTask接口实现。2.4 Thread的常用方法 3. 线程同步3.1. synchronized关键字3.1.1同步代码块:3.1.2 同步方法: 3.2. Lock接口 4. 线程间通信5. 线程池5.1 使…...
SpringCloud全面学习笔记之进阶篇
目录 前言微服务保护初识Sentinel雪崩问题及解决方案雪崩问题超时处理仓壁模式熔断降级流量控制总结 服务保护技术对比Sentinel介绍和安装微服务整合Sentinel 流量控制快速入门流控模式关联模式链路模式小结 流控效果warm up排队等待 热点参数限流全局参数限流热点参数限流案例…...
英语中主语从句的概念及其用法,例句(不断更新)
主语从句的原理 主语从句是一种充当整个句子主语的从句,主语从句构成的句子,是要以引导词开头的。它可以用名词性从属连词、关系代词或关系副词引导。主语从句通常位于谓语动词之前,用于表示动作、状态或事件的主体。 以下是一些常用的引导主…...
数组的子数组(亚阵列)、子序列,字符串的子串、子序列
数组 子数组 一个或连续多个数组中的元素组成一个子数组 子数组最少包含一个元素 数组 {1, 2, 3} 的子数组是 {1}, {1, 2}, {1, 2, 3}, {2}, {2, 3}, 和 {3}. 子序列 子序列就是在原来序列中找出一部分组成的序列 子序列不一定连续 相对位置还是不变 但是ÿ…...
MySQL 知识:迁移数据目录到其他路径
一、系统环境 操作系统:Centos 7 已安装环境:MySQL 8.0.26 二、开始操作 2.1 关闭SELinux 为了提高 Linux 系统的安全性,在 Linux 上通常会使用 SELinux 或 AppArmor 实现强制访问控制(Mandatory Access Control MACÿ…...
Go | 一分钟掌握Go | 8 - 并发
作者:Mars酱 声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。 转载:欢迎转载,转载前先请联系我! 前言 当今编程界,一个好的编译型语言如果不支持并发,…...
【滤波】多元高斯
本文主要翻译自rlabbe/Kalman-and-Bayesian-Filters-in-Python的第5章节05-Multivariate-Gaussians(多元高斯)。 %matplotlib inline#format the book import book_format book_format.set_style()简介 上一篇文章中的技术非常强大,但它们只…...
单源最短路问题
全部代码 全部代码在github acwing 上 正在更新 https://github.com/stolendance/acwing 图论 欢迎大家star与fork 单源最短路问题 先用spfa算法 不行再换其他的 spfa-超级万能 说不定比dijsktra还快 dis[] 代表第k到某一点的最短距离 queue 代表刚被更新的点 它有可能更…...
Security方法注解权限控制过程及自定义权限表达式
文章目录 使用内置的权限表达式PreAuthorizePermissionEvaluator 自定义权限表达式SysMethodSecurityExpressionHandler源码流程 SysMethodSecurityExpressionRoot 使用内置的权限表达式 PreAuthorize 这个用来判断超级管理员的话,还得在表达式上加上或 Permissi…...
vue 省市县三级联动
1、 <template><div>所在省<el-select popper-class"eloption" :popper-append-to-body"true"change"getShiList(obj.province)" v-model"obj.province" placeholder"请选择所在省" clearableclear"re…...
ChatGPT实现编程语言转换
编程语言转换 对于程序员来说,往往有一类工作,是需要将一部分业务逻辑实现从服务端转移到客户端,或者从客户端转移到服务端。这类工作,通常需要将一种编程语言的代码转换成另一种编程语言的代码,这就需要承担这项工作…...
浅拷贝和深拷贝
浅拷贝: 定义:浅拷贝(Shallow Copy)是一种简单的对象复制方式,将一个对象的数据成员直接复制给另一个对象(通常是通过默认的复制构造函数或赋值运算符实现),这些数据成员可以是基本…...
进程地址空间与页表方面知识点(缺页中断及写时拷贝部分原理)
谢谢阅读,如有错误请大佬留言!! 目录 谢谢阅读,如有错误请大佬留言!! 抛出总结 开始介绍 发现问题 进程地址空间(虚拟地址) 页表 物理内存与进程地址空间映射 缺页中断基本…...
Photoshop如何使用滤镜之实例演示?
文章目录 0.引言1.将普通照片制作成油画效果2.使用液化滤镜修出完美身材3.用镜头光晕滤镜制作唯美的逆光人像4.用Camera Raw滤镜对偏色风景照进行调色 0.引言 因科研等多场景需要进行绘图处理,笔者对PS进行了学习,本文通过《Photoshop2021入门教程》及其…...
Flutter 组件抽取:日期(DatePicker)、时间(TimePicker)弹窗选择器【仿照】
简介 仿照《Flutter 仿ios自定义一个DatePicker》实行的日期弹窗选择器(DatePicker)、时间弹窗选择器(TimePicker) 效果 范例 class _TestPageState extends State<TestPage> {overridevoid initState() {super.initStat…...
基于opencv的YOLOV3对图片的目标检测
目录 1. 准备工作 2. utils 函数 2.1 plot_show 函数 2.2 get_prediction 函数 2.3 draw_bounding_box 绘制边界框函数...
Mermaid流程图
所有流程图都由节点,几何形状和边缘,箭头或线条组成。mermaid代码定义了这些节点和边缘的制作和交互方式。 它还可以容纳不同的箭头类型、多方向箭头以及与子图之间的链接。 1、流程图的方向 TB - 从上到下TD - 自上而下/与上到下相同BT - 从下到上RL -…...
国产!全志科技T507-H工业核心板( 4核ARM Cortex-A5)规格书
1核心板简介 创龙科技 SOM-TLT507 是一款基于全志科技 T507-H 处理器设计的 4 核 ARM Cortex-A 53 全国产工业核心板,主频高达 1.416GHz 。核心板 CPU 、ROM 、RAM、电源、晶振等所有元器件均采用国产工业级方案,国产化率 100%。 核心板通过邮票孔连接方式引出 MIPI CSI 、…...
java小记 2023-05-05
public class Test {/*** 谓类的方法就是指类中用static 修饰的方法(非static 为实例方法),比如main 方法,那么可以以main* 方法为例,可直接调用其他类方法,必须通过实例调用实例方法,this 关键…...
CentOS安装Nginx
准备工作 在安装Nginx之前,我们需要进行一些准备工作: 确认系统是否已经安装了Nginx。如果已经安装了,需要卸载掉旧版本。安装EPEL源,以获取Nginx的软件包。安装必要的依赖软件包。 卸载旧版Nginx 如果已经安装了旧版本的Ngin…...
CSS布局基础(CSS书写顺序 导航栏写法 常见问题)
CSS布局基础(CSS书写顺序 & 导航栏写法) CSS布局基础(CSS书写顺序)导航栏写法PC端网页开发一般步骤容易出问题的点 CSS布局基础(CSS书写顺序) 布局定位属性自身属性(宽高,边框&…...
打造卓越 QML 层级设计:从入门到精通
目录标题 引言:QML 层级设计的重要性1.1 什么是 QML1.2 层级设计的核心理念1.3 实际应用案例 QML 基础知识2.1 语言概述2.2 基本元素2.3 属性和信号 设计原则与规范3.1 命名规范3.1.1 标识符命名3.1.2 文件命名3.1.3 文件夹命名 3.2 代码风格3.2.1 缩进与空格3.2.2 …...
shell流程控制之条件判断练习
1、判断当前磁盘剩余空间是否有20G,如果小于20G,则将报警邮件发送给管理员,每天检查一次磁盘剩余空间。 因为如果磁盘剩余空间小于20G需要报警发送邮件给管理员,所以需要对管理员的邮箱进行设置 (1)首先…...
linux中TF启动卡制作:磁盘分区文件同步
文章目录 前言:1. 连接TF卡2. 磁盘卸载载与分区2.1 磁盘卸载2.2 创建第一个分区2.3 创建第二个分区 3. 磁盘格式化4. 文件同步5. 检查与BOOT分区启动文件拷贝总结: 前言: TF卡在linux环境下配置好相关软件后,把配置好的系统以及软…...
【操作系统OS】学习笔记:第一章 操作系统基础【哈工大李治军老师】
基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记,仅进行交流分享。 特此鸣谢李治军老师,操作系统的神作! 如果本篇笔记帮助到了你,还请点赞 关注 支持一下 ♡>𖥦<)!! 主页专栏有更多࿰…...
Linux C/C++ 网络编程中地址格式转换(inet_pton和inet_ntop函数)
网络编程中地址格式转换(inet_pton和inet_ntop函数) 地址格式转换 #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>int inet_pton(int af , const char * src ,void * dst);(1…...
庖丁解牛函数知识---C语言《2》
目录 前言: 1.嵌套调用函数 2.链式访问 3.函数的声明与定义 4.*递归 5.递归与非递归 ❤博主CSDN:啊苏要学习 ▶专栏分类:C语言◀ C语言的学习,是为我们今后学习其它语言打好基础,C生万物! 开始我们的C语言之旅吧…...
Git 使用教程:最详细、最正宗手把手教学(万字长文)
目录 一:Git二:SVN与Git的的区别三、安装Git四:常规操作五:远程仓库六:创建与合并分支七:bug分支八:多人协作九:git可视化工具 Git Git 是一种分布式版本控制系统,用于…...
【华为OD机试 2023最新 】最优资源分配/芯片资源占用(C语言题解 100%)
文章目录 题目描述输入描述输出描述备注用例题目解析代码思路C语言题目描述 某块业务芯片最小容量单位为1.25G,总容量为M*1.25G,对该芯片资源编号为1,2,…,M。该芯片支持3种不同的配置,分别为A、B、C。 配置A:占用容量为 1.25 * 1 = 1.25G配置B:占用容量为 1.25 * 2 =…...
markdown二元运算符
符号markdown名称 \pm \pm正负/加减 ∓ \mp ∓\mp负正/减加 \times \times乘号 ⋅ \cdot ⋅\cdot点乘号 \div \div除号 ∣ \mid ∣\mid整除 ∤ \nmid ∤\nmid不整除 ⊕ \oplus ⊕\oplus异或...
php网站导航/seo投放
日期类型的自变量通常可以用以下几种方法进行处理: 独热编码:将日期类型的自变量转换成若干个二元组变量,每个二元组变量表示一个可能的日期值,并将该值编码成 1,其他值编码成 0。 数值化:将日期类型的自变…...
asp网站制作实例教程/如何做品牌宣传与推广
数据库之存储过程和存储函数(六) 什么是存储过程 存储过程是一组为了完成某项特定功能的SQL语句集,其实质就是一段存储在数据库中的代码。它可以由声明式的sql语句和过程式sql语句组成。 优点 1. 可以增强sql语言的功能和灵活性 2. 良好的封装性3. 高性能4. 减少…...
西宁建站/微信营销软件排行榜
文章目录1.分布式微服务项目是如何设计的2.cookie和session的区别,如何用session进行身份验证3.token,jwt,如何通过token进行身份验证4.为什么token可以预防CSRF,cookie无法防止5.分布式下,session共享方案1.分布式微服务项目是如何设计的 1.负载层 2.业务层 3.能力层(中台) 4…...
建设一个网站的基本步骤/企业内训机构
软件名称识别[abrt-addon-ccpp]-[2.1.11-19].[el7].[x86_64].rpm|| || || ||软件名称 软件版本 适用版本 软件适用系统 64位2.如何安装软件1.rpmrpm -ivh name.rpm 安装 ,-v显示过程,…...
建手机号码的网站/网站权重优化
Batch、Epoch和IterationBatch(批次)Epoch(轮次)Iteration(迭代)在深度学习中,Batch、Epoch和Iteration是非常重要的概念,它们是训练模型时的三个基本单位。以下是它们的概念、区别和…...
中小企业网站查询/网站维护主要做什么
传统超级计算机:“看上去很美”“盲目追求Linpack排名的炒作、无休止的价格战、热衷堆砌集群硬件规模,把软件和应用全推给客户、忽视对HPC应用人才培养,掠夺性策略..”在3月2日浪潮“倚天”万亿次桌面超级计算机发布会上,浪潮高性…...