JUC面试知识点手册

第一章:Java并发简介

1.1 什么是并发编程

并发编程是指在同一时间段内执行多个任务的编程方式。在单核处理器上,并发通过时间分片来实现,即在同一时间只有一个任务在执行,其他任务被暂停等待。在多核处理器上,并发可以通过同时执行多个任务来实现。

并发编程的主要目的是提高程序的执行效率,特别是在处理I/O操作、网络请求或大规模计算时,并发可以显著减少程序的响应时间。

并发编程的挑战

  • 线程安全:多个线程访问共享资源时,可能会导致数据不一致问题。
  • 死锁:多个线程互相等待对方释放资源,导致程序永远无法继续执行。
  • 线程饥饿:某些线程长时间无法获得执行机会。
  • 上下文切换:线程切换带来的开销,可能导致性能下降。
1.2 Java中的并发编程模型

Java提供了多种并发编程模型,以帮助开发者实现并发处理。主要的模型包括:

  1. 线程(Thread)

    • Java中的每个线程都是操作系统级别的线程,由java.lang.Thread类来表示。开发者可以通过继承Thread类或实现Runnable接口来创建线程。

    • 示例

      public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running");}public static void main(String[] args) {MyThread thread = new MyThread();thread.start();}
      }
      
  2. 线程池(Thread Pool)

    • 线程池是一种管理线程的机制,它避免了频繁创建和销毁线程的开销,提高了程序的执行效率。Java通过Executor框架提供了线程池的支持。

    • 示例

      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++) {executor.submit(() -> {System.out.println("Thread is running");});}executor.shutdown();}
      }
      
  3. 并发工具包(java.util.concurrent)

    • Java提供了java.util.concurrent包,内含大量并发工具类,如锁机制(ReentrantLock)、同步工具(CountDownLatchCyclicBarrier)、并发集合(ConcurrentHashMap)等,这些工具类简化了并发编程中的复杂性。
1.3 线程的生命周期

Java中的线程生命周期可以分为以下几个状态:

  1. 新建(New):线程对象创建后,还未调用start()方法。
  2. 就绪(Runnable):调用start()方法后,线程进入就绪状态,等待CPU分配执行时间。
  3. 运行(Running):线程获得CPU时间片后,开始执行run()方法中的代码。
  4. 阻塞(Blocked):线程等待某些资源(如锁)时,会进入阻塞状态,等待资源变为可用。
  5. 等待(Waiting):线程通过调用wait()方法进入等待状态,等待其他线程的通知(notify()notifyAll())。
  6. 超时等待(Timed Waiting):线程通过调用sleep()wait(long timeout)等方法进入超时等待状态,指定时间后自动返回就绪状态。
  7. 终止(Terminated):线程的run()方法执行完毕,或者因异常退出,线程进入终止状态,无法再执行。
第一章小结

本章主要介绍了并发编程的基本概念以及Java中提供的并发编程模型。理解这些基础概念对于后续深入学习JUC相关知识点非常重要。下一章将深入探讨Java中用于实现同步的基础工具。
在这里插入图片描述

第二章:基础同步工具

在并发编程中,确保多个线程在访问共享资源时不会引起数据不一致问题是至关重要的。Java提供了一些基础同步工具来帮助开发者管理线程间的同步。在本章中,我们将详细介绍SynchronizedVolatile这两个最基本的同步工具。

2.1 synchronized关键字
2.1.1 原理

synchronized是Java中用于实现线程同步的关键字,它可以用来修饰方法或代码块,以确保同一时间只有一个线程能够执行被修饰的代码。这是通过对象的监视器(Monitor)锁来实现的。

每个对象都有一个隐含的监视器锁,当一个线程试图执行一个被synchronized修饰的方法或代码块时,它首先必须获得该对象的监视器锁。一旦获得锁,其他线程将无法访问该对象的任何其他同步方法或代码块,直到锁被释放。

2.1.2 应用场景

synchronized关键字主要用于以下场景:

  • 线程安全的类:如果某个类的多个线程实例可能会同时访问共享资源,就需要使用synchronized来保证线程安全。
  • 临界区保护:在某些需要确保数据一致性的代码段,可以通过synchronized来实现对临界区的保护。
2.1.3 代码实现与示例
示例1:方法级别的synchronized
public class SynchronizedExample {private int counter = 0;// 使用synchronized修饰方法,确保线程安全public synchronized void increment() {counter++;}public int getCounter() {return counter;}public static void main(String[] args) {SynchronizedExample example = new SynchronizedExample();// 启动两个线程进行递增操作Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();// 等待线程执行完毕try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出结果System.out.println("Counter: " + example.getCounter());}
}

在这个示例中,increment()方法被synchronized修饰,确保了在多线程环境下counter变量的递增操作是线程安全的。

示例2:代码块级别的synchronized
public class SynchronizedBlockExample {private final Object lock = new Object();private int counter = 0;public void increment() {synchronized (lock) {counter++;}}public int getCounter() {return counter;}public static void main(String[] args) {SynchronizedBlockExample example = new SynchronizedBlockExample();// 启动两个线程进行递增操作Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();// 等待线程执行完毕try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出结果System.out.println("Counter: " + example.getCounter());}
}

在这个示例中,synchronized被应用于代码块级别,通过锁对象lock来实现同步。这种方式的好处是可以灵活控制锁的粒度,避免不必要的锁竞争。

2.2 volatile关键字
2.2.1 原理

volatile关键字是用于声明变量的一个修饰符,用来确保变量在多个线程之间的可见性。当一个变量被声明为volatile时,所有线程在读取该变量时会直接从主内存中读取,而不是从线程的工作内存中缓存。

此外,volatile还会禁止指令重排序,确保了变量的赋值操作在程序中按预期顺序执行。

2.2.2 应用场景

volatile适用于以下场景:

  • 状态标记:当某个标记变量需要在多个线程之间共享时,可以使用volatile来确保其可见性。
  • 轻量级同步:在某些情况下,使用volatile可以避免使用锁,从而提高性能。
2.2.3 代码实现与示例
示例1:volatile状态标记
public class VolatileExample {private static volatile boolean flag = true;public static void main(String[] args) {Thread t1 = new Thread(() -> {while (flag) {// 等待flag变为false}System.out.println("Thread 1 has exited.");});Thread t2 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}flag = false; // 修改flag的值System.out.println("Flag has been set to false.");});t1.start();t2.start();}
}

在这个示例中,flag变量被声明为volatile,确保了线程t1能够及时看到线程t2flag变量的修改,避免了无限循环的发生。

示例2:防止指令重排序
public class ReorderingExample {private int x = 0;private volatile boolean flag = false;public void writer() {x = 42;              // 1flag = true;         // 2}public void reader() {if (flag) {          // 3System.out.println(x);  // 4}}public static void main(String[] args) {ReorderingExample example = new ReorderingExample();Thread t1 = new Thread(example::writer);Thread t2 = new Thread(example::reader);t1.start();t2.start();}
}

在这个示例中,flag被声明为volatile,保证了线程t2在判断flagtrue时,能够看到线程t1x的赋值操作,避免了由于指令重排序引发的不可预测行为。

第二章小结

本章介绍了Java中用于实现同步的两种基础工具:synchronizedvolatilesynchronized通过对象的监视器锁实现了对临界区的保护,确保了线程的安全性。而volatile则通过保证变量的可见性和防止指令重排序,为多线程环境提供了轻量级的同步机制。

下一章将深入探讨Java中的锁机制,包括ReentrantLockReadWriteLockStampedLock等高级锁的应用与实现。
在这里插入图片描述

第三章:锁机制

在并发编程中,锁机制是确保多线程访问共享资源时避免数据不一致和竞争条件的重要工具。Java中的JUC(Java并发工具包)提供了多种锁实现,以应对不同的并发场景。在本章中,我们将详细介绍ReentrantLockReadWriteLockStampedLock的原理、应用场景及代码实现。

3.1 ReentrantLock
3.1.1 原理

ReentrantLock 是一个可重入的、独占锁。所谓可重入,意味着同一线程可以多次获取同一个锁而不会发生死锁。ReentrantLock 提供了与synchronized类似的功能,但它更加灵活,可以响应中断请求、支持公平锁和非公平锁、以及提供了尝试获取锁的功能。

  • 公平锁:线程按照请求锁的顺序获取锁。
  • 非公平锁:线程可以“插队”,抢占锁的机会更大,可能导致某些线程长期得不到锁。
3.1.2 应用场景

ReentrantLock 适用于需要更精细控制锁的场景,例如:

  • 需要响应中断的锁操作
  • 需要超时获取锁
  • 需要公平锁机制
  • 复杂的同步控制结构
3.1.3 代码实现与示例
示例1:使用ReentrantLock实现线程安全
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int counter = 0;public void increment() {lock.lock(); // 获取锁try {counter++;} finally {lock.unlock(); // 释放锁}}public int getCounter() {return counter;}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 启动两个线程进行递增操作Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});t1.start();t2.start();// 等待线程执行完毕try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出结果System.out.println("Counter: " + example.getCounter());}
}

在这个示例中,我们使用ReentrantLock来保护对counter变量的操作,确保它在线程间的操作是安全的。

示例2:使用ReentrantLock响应中断
import java.util.concurrent.locks.ReentrantLock;public class InterruptibleLockExample {private final ReentrantLock lock = new ReentrantLock();public void performTask() {try {lock.lockInterruptibly(); // 可以响应中断的锁获取try {// 业务逻辑代码System.out.println(Thread.currentThread().getName() + " is performing a task.");} finally {lock.unlock();}} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " was interrupted.");}}public static void main(String[] args) {InterruptibleLockExample example = new InterruptibleLockExample();Thread t1 = new Thread(example::performTask);Thread t2 = new Thread(example::performTask);t1.start();t2.start();// 中断其中一个线程t2.interrupt();}
}

在这个示例中,ReentrantLocklockInterruptibly()方法允许线程在等待锁的过程中响应中断。这种机制在需要快速响应中断信号的应用场景中非常有用。

3.2 ReadWriteLock
3.2.1 原理

ReadWriteLock 是一种分离读锁和写锁的锁机制。它包含两个锁:一个读锁(共享锁)和一个写锁(独占锁)。多个线程可以同时获取读锁,但只有一个线程可以获取写锁,且在写锁被持有期间,其他线程不能获取读锁。

  • 读锁:允许多个线程同时读取,不会造成冲突。
  • 写锁:只允许一个线程写入,其他线程不能读取或写入。
3.2.2 应用场景

ReadWriteLock 非常适用于读多写少的场景,例如:

  • 配置数据的读取和更新
  • 缓存的访问和刷新
  • 大量读操作、少量写操作的场景
3.2.3 代码实现与示例
示例1:使用ReadWriteLock实现高效读写
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReadWriteLock lock = new ReentrantReadWriteLock();private int value = 0;public void readValue() {lock.readLock().lock(); // 获取读锁try {System.out.println(Thread.currentThread().getName() + " read value: " + value);} finally {lock.readLock().unlock(); // 释放读锁}}public void writeValue(int newValue) {lock.writeLock().lock(); // 获取写锁try {value = newValue;System.out.println(Thread.currentThread().getName() + " wrote value: " + value);} finally {lock.writeLock().unlock(); // 释放写锁}}public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();Runnable readTask = example::readValue;Runnable writeTask = () -> example.writeValue((int) (Math.random() * 100));// 启动多个读线程for (int i = 0; i < 5; i++) {new Thread(readTask).start();}// 启动一个写线程new Thread(writeTask).start();}
}

在这个示例中,多个线程可以同时获取读锁并读取值,而写锁则确保了只有一个线程可以同时写入值,避免了并发写入时的冲突。

3.3 StampedLock
3.3.1 原理

StampedLock 是一种改进的读写锁,除了传统的读锁和写锁外,还提供了一种乐观读锁。乐观读锁允许在没有实际加锁的情况下读取数据,在数据没有发生变化时,可以避免加锁的开销,从而提高性能。

  • 乐观读锁:初始状态下不加锁,允许读操作。如果在读的过程中没有其他线程写入,则读取成功,否则需要升级为悲观读锁或重新读取。
  • 悲观读锁:与ReadWriteLock中的读锁类似。
  • 写锁:与ReadWriteLock中的写锁类似。
3.3.2 应用场景

StampedLock 适用于高并发的读操作,尤其是在大多数情况下读操作不会受到写操作影响的场景。例如:

  • 数据的监控与分析系统
  • 大数据处理中的并行计算
3.3.3 代码实现与示例
示例1:使用StampedLock的乐观读锁
import java.util.concurrent.locks.StampedLock;public class StampedLockExample {private final StampedLock lock = new StampedLock();private int value = 0;public int readValue() {long stamp = lock.tryOptimisticRead(); // 尝试获取乐观读锁int currentValue = value; // 读取共享变量if (!lock.validate(stamp)) { // 检查在读取过程中是否有写操作stamp = lock.readLock(); // 获取悲观读锁try {currentValue = value;} finally {lock.unlockRead(stamp); // 释放悲观读锁}}return currentValue;}public void writeValue(int newValue) {long stamp = lock.writeLock(); // 获取写锁try {value = newValue;} finally {lock.unlockWrite(stamp); // 释放写锁}}public static void main(String[] args) {StampedLockExample example = new StampedLockExample();Runnable readTask = () -> {System.out.println(Thread.currentThread().getName() + " read value: " + example.readValue());};Runnable writeTask = () -> example.writeValue((int) (Math.random() * 100));// 启动多个读线程for (int i = 0; i < 5; i++) {new Thread(readTask).start();}// 启动一个写线程new Thread(writeTask).start();}
}

在这个示例中,StampedLock的乐观读锁允许多个线程以最小的开销读取数据,并且在数据没有被其他线程修改的情况下,可以避免升级为悲观读锁,提高了系统的整体性能。

第三章小结

本章详细介绍了Java中的几种主要锁机制,包括ReentrantLockReadWriteLockStampedLock。这些锁为不同的并发场景提供了灵活而高效的解决方案,帮助开发者在复杂的并发环境中保持数据的一致性和系统的高性能。

下一章将介绍Java中的同步辅助工具,如CountDownLatchCyclicBarrierSemaphore等,它们在多线程协作中扮演了重要角色。
在这里插入图片描述

第四章:同步辅助工具

在并发编程中,线程之间的协作是非常重要的一部分。Java提供了多种同步辅助工具,用于管理线程的协作与协调。本章将详细介绍CountDownLatchCyclicBarrierSemaphoreExchanger的原理、应用场景及代码实现。

4.1 CountDownLatch
4.1.1 原理

CountDownLatch 是一种同步辅助工具,用于等待一组线程完成操作后再继续执行。它通过一个计数器来实现,该计数器初始值为线程的数量,每当一个线程完成其操作时,计数器减1。当计数器达到零时,所有等待的线程才会继续执行。

4.1.2 应用场景

CountDownLatch 适用于需要等待多个线程执行完成后再继续执行的场景。例如:

  • 主线程等待多个工作线程完成初始化
  • 测试用例中等待多个服务启动后执行测试
  • 并行计算中等待各个子任务完成后再汇总结果
4.1.3 代码实现与示例
示例1:主线程等待多个线程完成
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threadCount = 3;CountDownLatch latch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " is working.");try {Thread.sleep(1000); // 模拟工作任务} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown(); // 减少计数}}).start();}// 主线程等待其他线程完成latch.await();System.out.println("All threads have finished.");}
}

在这个示例中,CountDownLatch用于等待三个工作线程完成各自的任务后,再让主线程继续执行。

4.2 CyclicBarrier
4.2.1 原理

CyclicBarrier 是一种同步辅助工具,它允许一组线程互相等待,直到所有线程都达到一个共同的屏障点。与CountDownLatch不同的是,CyclicBarrier可以重复使用,即当所有线程都达到屏障后,它可以被重置,以便下次使用。

4.2.2 应用场景

CyclicBarrier 适用于需要多个线程并肩作战、阶段性同步的场景,例如:

  • 并行计算中的分段任务,每个阶段都需要同步
  • 多线程游戏中等待玩家都准备好后再继续
  • 模拟并发用户场景,等待所有用户就绪后一起执行
4.2.3 代码实现与示例
示例1:多线程阶段性同步
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {int threadCount = 3;CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {System.out.println("All threads reached the barrier, proceeding to the next step.");});for (int i = 0; i < threadCount; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");barrier.await(); // 等待其他线程System.out.println(Thread.currentThread().getName() + " passed the barrier.");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}}
}

在这个示例中,三个线程在执行完各自的任务后,等待其他线程到达屏障点。所有线程都到达后,它们将继续执行下一个任务。

4.3 Semaphore
4.3.1 原理

Semaphore 是一种用于控制同时访问特定资源的线程数量的同步工具。它通过维护一个许可的计数器来控制资源的访问数量,线程在进入临界区前必须获得许可,离开时归还许可。

4.3.2 应用场景

Semaphore 适用于需要限制资源访问数量的场景,例如:

  • 限制数据库连接池的连接数量
  • 控制对某个服务的并发访问
  • 实现限流机制
4.3.3 代码实现与示例
示例1:限制对资源的并发访问
import java.util.concurrent.Semaphore;public class SemaphoreExample {private static final int MAX_CONCURRENT_THREADS = 2;private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_THREADS);public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {try {semaphore.acquire(); // 获取许可System.out.println(Thread.currentThread().getName() + " acquired a permit.");Thread.sleep(2000); // 模拟工作任务} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + " released a permit.");semaphore.release(); // 释放许可}}).start();}}
}

在这个示例中,Semaphore限制了最多只能有两个线程同时访问某个资源,其余线程将等待直到有可用的许可。

4.4 Exchanger
4.4.1 原理

Exchanger 是一种用于在两个线程之间交换数据的同步工具。它提供了一个同步点,两个线程可以在这个点上交换数据。两个线程必须都到达交换点,否则它们将一直等待。

4.4.2 应用场景

Exchanger 适用于需要两个线程之间交换数据的场景,例如:

  • 生产者和消费者之间的数据交换
  • 数据处理的流水线,每个步骤由不同线程执行
  • 在多线程游戏中交换玩家的状态信息
4.4.3 代码实现与示例
示例1:线程之间的数据交换
import java.util.concurrent.Exchanger;public class ExchangerExample {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(() -> {String data = "Data from Thread 1";try {String receivedData = exchanger.exchange(data);System.out.println("Thread 1 received: " + receivedData);} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {String data = "Data from Thread 2";try {String receivedData = exchanger.exchange(data);System.out.println("Thread 2 received: " + receivedData);} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}

在这个示例中,两个线程分别准备了一段数据,并在同步点上交换了彼此的数据。

第四章小结

本章详细介绍了Java中的几种同步辅助工具,包括CountDownLatchCyclicBarrierSemaphoreExchanger。这些工具在多线程协作中起到了关键作用,帮助开发者有效地管理线程之间的协调和资源的共享。

下一章将介绍Java中的原子类和无锁编程,包括AtomicIntegerAtomicLongAtomicReference等,它们提供了一种高效且线程安全的并发处理方式。
在这里插入图片描述

第五章:原子类和无锁编程

无锁编程是并发编程中的一个重要方向,Java通过java.util.concurrent.atomic包提供了一些原子类,帮助开发者在高并发场景下实现高效的线程安全操作。这些原子类通过CAS(Compare-And-Swap)算法来实现原子操作,无需使用传统的锁机制。

5.1 AtomicIntegerAtomicLong
5.1.1 原理

AtomicIntegerAtomicLong 是提供整数类型原子操作的类。它们内部通过CAS操作保证了对整数的增减、赋值等操作的原子性。这些操作在多线程环境下不会出现竞争条件。

5.1.2 应用场景

AtomicIntegerAtomicLong 适用于需要高效进行整数操作且不希望使用锁的场景,例如:

  • 高并发计数器
  • 基于CAS的乐观锁
  • 实现自定义的原子操作
5.1.3 代码实现与示例
示例1:使用AtomicInteger实现高并发计数器
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {private static final AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) {for (int i = 0; i <10; i++) {new Thread(() -> {for (int j = 0; j < 100; j++) {counter.incrementAndGet(); // 原子性递增}}).start();}// 等待线程执行完毕try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final counter value: " + counter.get());}
}

在这个示例中,AtomicInteger确保了多个线程对同一计数器的操作是线程安全的。

5.2 AtomicReference
5.2.1 原理

AtomicReference 是一个可以保证引用类型变量原子性的类。它通过CAS算法保证了对对象引用的更新操作在多线程环境下的安全性。AtomicReference 允许开发者在高并发场景下安全地进行对象的引用更新操作。

5.2.2 应用场景

AtomicReference 适用于需要安全更新共享对象引用的场景,例如:

  • 无锁的对象更新操作
  • 实现不可变对象的替换
  • 在并发环境中安全地交换对象引用
5.2.3 代码实现与示例
示例1:使用AtomicReference实现无锁对象更新
import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceExample {private static final AtomicReference<String> reference = new AtomicReference<>("initial");public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {String oldValue = reference.get();String newValue = oldValue + "-updated";if (reference.compareAndSet(oldValue, newValue)) {System.out.println("Updated reference to: " + newValue);} else {System.out.println("Failed to update reference.");}}).start();}}
}

在这个示例中,AtomicReference通过CAS操作保证了对字符串引用的更新是线程安全的。

第五章小结

本章介绍了Java中的原子类和无锁编程的基本原理与实现。通过使用AtomicIntegerAtomicLongAtomicReference等类,开发者可以在高并发场景下实现高效的线程安全操作,而无需使用传统的锁机制。

下一章将介绍线程池及其应用,包括ThreadPoolExecutorScheduledThreadPoolExecutor,帮助开发者更好地管理多线程环境下的线程资源。
在这里插入图片描述

第六章:线程池及其应用

线程池是Java并发编程中非常重要的工具,它通过复用线程来提高系统的效率,并避免了频繁创建和销毁线程的开销。本章将介绍ThreadPoolExecutorScheduledThreadPoolExecutor的原理、应用场景及代码实现。

6.1 ThreadPoolExecutor
6.1.1 原理

ThreadPoolExecutor 是Java中最强大的线程池实现类。它允许开发者通过配置核心线程数、最大线程数、线程存活时间、任务队列等参数来自定义线程池的行为。

ThreadPoolExecutor 的工作机制如下:

  • 核心线程数:线程池中始终保持存活的线程数量。
  • 最大线程数:线程池中允许的最大线程数量。
  • 任务队列:当核心线程数已满时,新的任务将被放入任务队列中等待执行。
  • 线程存活时间:当线程数超过核心线程数时,多余的线程在空闲一段时间后将被终止。
6.1.2 应用场景

ThreadPoolExecutor 适用于需要动态管理线程的场景,例如:

  • 服务器请求处理
  • 并行任务执行
  • 高并发任务的调度和管理
6.1.3 代码实现与示例
示例1:使用ThreadPoolExecutor管理线程
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorExample {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数5, // 最大线程数60, // 空闲线程存活时间TimeUnit.SECONDS,new LinkedBlockingQueue<>(10) // 任务队列);for (int i = 0; i < 15; i++) {final int index = i;executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " is processing task " + index);try {Thread.sleep(2000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown(); // 关闭线程池}
}

在这个示例中,ThreadPoolExecutor被配置为一个拥有2个核心线程、5个最大线程和一个容量为10的任务队列的线程池。线程池管理着并发任务的执行。

6.2 ScheduledThreadPoolExecutor
6.2.1 原理

ScheduledThreadPoolExecutor 是一个能够调度任务在指定时间或周期性执行的线程池。它扩展了ThreadPoolExecutor,支持任务的定时调度和周期性执行。

6.2.2 应用场景

ScheduledThreadPoolExecutor 适用于需要定时或周期性执行任务的场景,例如:

  • 定时任务调度
  • 周期性数据刷新
  • 定时触发事件
6.2.3 代码实现与示例
示例1:使用ScheduledThreadPoolExecutor实现定时任务
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ScheduledThreadPoolExecutorExample {public static void main(String[] args) {ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(2);scheduler.scheduleAtFixedRate(() -> {System.out.println("Executing scheduled task at " + System.currentTimeMillis());}, 1, 3, TimeUnit.SECONDS); // 初始延迟1秒,之后每隔3秒执行一次// 在示例中,线程池不会被显式关闭,以便观察定时任务的执行}
}

在这个示例中,ScheduledThreadPoolExecutor被配置为每隔3秒执行一次定时任务。

第六章小结

本章介绍了Java中的线程池及其应用。通过使用ThreadPoolExecutorScheduledThreadPoolExecutor,开发者可以有效地管理多线程环境下的线程资源,提升系统的性能和稳定性。

下一章将介绍Fork/Join框架及其在并行计算中的应用,包括如何使用ForkJoinPoolForkJoinTask实现大规模数据的并行处理。
在这里插入图片描述

第七章:Fork/Join框架

Fork/Join框架是Java 7引入的一个并行计算框架,用于将大任务拆分为小任务并行处理,以充分利用多核处理器的计算能力。本章将介绍ForkJoinPoolForkJoinTask的原理、应用场景及代码实现。

7.1 ForkJoinPool
7.1.1 原理

ForkJoinPool 是一个支持并行任务处理的线程池。它实现了工作窃取算法(Work Stealing),即当一个线程完成了自己分配的任务后,它可以窃取其他线程还未完成的任务来执行,以提高整体的任务处理效率。

7.1.2 应用场景

ForkJoinPool 适用于需要将大任务分解为多个子任务并行处理的场景,例如:

  • 大规模数据处理
  • 递归算法的并行化
  • 并行搜索和排序
7.1.3 代码实现与示例
示例1:使用ForkJoinPool实现并行求和
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;public class ForkJoinSumExample {private static class SumTask extends RecursiveTask<Integer> {private static final int THRESHOLD = 10;private final int[] array;private final int start;private final int end;public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Integer compute() {if (end - start <= THRESHOLD) {int sum = 0;for (int i = start; i <= end; i++) {sum += array[i];}return sum;} else {int mid = (start + end) / 2;SumTask leftTask = new SumTask(array, start, mid);SumTask rightTask = new SumTask(array, mid + 1, end);leftTask.fork(); // 拆分子任务intrightResult = rightTask.compute(); // 执行子任务int leftResult = leftTask.join(); // 获取左任务结果return leftResult + rightResult;}}}public static void main(String[] args) {int[] array = new int[100];for (int i = 0; i < array.length; i++) {array[i] = i + 1;}ForkJoinPool forkJoinPool = new ForkJoinPool();SumTask sumTask = new SumTask(array, 0, array.length - 1);int result = forkJoinPool.invoke(sumTask);System.out.println("Sum: " + result);}
}

在这个示例中,ForkJoinPool用于并行求和,将数组的求和任务拆分为多个子任务并行执行。

第七章小结

本章介绍了Java中的Fork/Join框架及其在并行计算中的应用。通过使用ForkJoinPoolForkJoinTask,开发者可以高效地将大任务拆分为小任务并行处理,充分利用多核处理器的计算能力。

下一章将介绍并发集合及其在高并发场景中的应用,包括ConcurrentHashMapCopyOnWriteArrayListConcurrentLinkedQueue等。
在这里插入图片描述

第八章:并发集合

并发集合是Java中为应对高并发场景而设计的一系列线程安全的集合类。与传统的集合类不同,并发集合类在实现上充分考虑了多线程环境下的性能优化。本章将介绍ConcurrentHashMapCopyOnWriteArrayListConcurrentLinkedQueue的原理、应用场景及代码实现。

8.1 ConcurrentHashMap
8.1.1 原理

ConcurrentHashMap 是Java中的一个线程安全的哈希表实现。它通过分段锁机制和CAS操作保证了在多线程环境下的高效读写操作。ConcurrentHashMap将整个哈希表分成多个段,每个段有自己的锁,这样在进行并发写操作时,只需锁定其中一个段,而不是锁定整个哈希表,从而提高了并发性能。

8.1.2 应用场景

ConcurrentHashMap 适用于需要高效读取和更新映射数据的高并发场景,例如:

  • 线程安全的缓存实现
  • 高并发访问的配置管理
  • 并发统计和分析
8.1.3 代码实现与示例
示例1:使用ConcurrentHashMap实现线程安全的缓存
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {private static final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();public static void main(String[] args) {cache.put("key1", "value1");cache.put("key2", "value2");Runnable task = () -> {String value = cache.get("key1");System.out.println(Thread.currentThread().getName() + " read value: " + value);cache.put("key1", value + "-updated");};// 启动多个线程并发访问缓存for (int i = 0; i < 5; i++) {new Thread(task).start();}}
}

在这个示例中,ConcurrentHashMap确保了多个线程并发访问和更新缓存数据的线程安全性。

8.2 CopyOnWriteArrayList
8.2.1 原理

CopyOnWriteArrayList 是一个线程安全的、基于数组的列表实现。它的核心思想是写时复制(Copy-On-Write),即在进行写操作时,会创建一个新的数组副本,修改完副本后再将其替换为原数组。这种机制确保了读操作的高效性,因为读操作无需加锁。

8.2.2 应用场景

CopyOnWriteArrayList 适用于读多写少的场景,例如:

  • 配置数据的读取
  • 事件监听器的管理
  • 需要频繁读取而修改较少的列表数据
8.2.3 代码实现与示例
示例1:使用CopyOnWriteArrayList实现线程安全的事件监听器管理
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {private static final List<String> listeners = new CopyOnWriteArrayList<>();public static void main(String[] args) {listeners.add("Listener1");listeners.add("Listener2");Runnable readTask = () -> {for (String listener : listeners) {System.out.println(Thread.currentThread().getName() + " notified " + listener);}};Runnable writeTask = () -> {listeners.add("Listener3");System.out.println(Thread.currentThread().getName() + " added Listener3");};// 启动多个读线程和写线程for (int i = 0; i < 3; i++) {new Thread(readTask).start();new Thread(writeTask).start();}}
}

在这个示例中,CopyOnWriteArrayList确保了在多个线程并发读取和修改监听器列表时的线程安全性。

8.3 ConcurrentLinkedQueue
8.3.1 原理

ConcurrentLinkedQueue 是一个无锁的线程安全队列,它基于非阻塞算法实现,通过CAS操作来保证队列的高效并发性。ConcurrentLinkedQueue是一个先进先出的队列,适用于多线程环境下的任务队列或消息队列。

8.3.2 应用场景

ConcurrentLinkedQueue 适用于需要高效并发访问的队列场景,例如:

  • 多线程任务调度
  • 高并发的消息处理
  • 非阻塞的任务队列
8.3.3 代码实现与示例
示例1:使用ConcurrentLinkedQueue实现并发任务调度
import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentLinkedQueueExample {private static final ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();public static void main(String[] args) {queue.add("Task1");queue.add("Task2");Runnable worker = () -> {String task;while ((task = queue.poll()) != null) {System.out.println(Thread.currentThread().getName() + " processed " + task);}};// 启动多个线程处理队列中的任务for (int i = 0; i < 3; i++) {new Thread(worker).start();}}
}

在这个示例中,ConcurrentLinkedQueue确保了多个线程并发处理任务时的高效性和线程安全性。

第八章小结

本章详细介绍了Java中的并发集合及其在高并发场景中的应用。通过使用ConcurrentHashMapCopyOnWriteArrayListConcurrentLinkedQueue,开发者可以在多线程环境下实现高效的集合操作,同时确保线程安全性。

下一章将介绍CompletableFuture与异步编程,以及如何使用CompletableFuture来实现复杂的异步任务处理。
在这里插入图片描述

第九章:CompletableFuture与异步编程

在现代应用程序中,异步编程已经成为一种常见的并发处理方式。Java 8引入的CompletableFuture类提供了一种强大的方式来编写异步代码,它不仅支持非阻塞的异步操作,还可以进行复杂的任务组合和处理。本章将详细介绍CompletableFuture的原理、应用场景及代码实现。

9.1 CompletableFuture的基础概念
9.1.1 原理

CompletableFutureFuture接口的扩展,它提供了更多的功能,使得编写异步代码更加简洁和强大。与传统的Future不同,CompletableFuture不仅可以处理单个异步任务,还可以将多个任务组合在一起,形成更复杂的异步操作流。

CompletableFuture主要依赖于以下几个核心概念:

  • 非阻塞异步操作:通过异步计算,避免阻塞主线程,从而提高应用程序的响应速度。
  • 任务组合:可以将多个异步任务组合成一个复杂的异步操作流。
  • 异常处理:支持对异步操作中的异常进行处理和恢复。
9.1.2 应用场景

CompletableFuture 适用于以下场景:

  • 并行处理多个异步任务,并将结果汇总
  • 异步调用远程服务,并根据结果执行后续操作
  • 实现复杂的异步操作流,如依赖关系、任务组合、异常处理等
9.2 CompletableFuture的创建与执行
9.2.1 创建异步任务

CompletableFuture 提供了多种方式来创建异步任务,例如runAsync()supplyAsync()方法。

示例1:创建简单的异步任务
import java.util.concurrent.CompletableFuture;public class CompletableFutureExample {public static void main(String[] args) {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {System.out.println("Executing task in " + Thread.currentThread().getName());});// 等待异步任务完成future.join();}
}

在这个示例中,runAsync()方法创建了一个不返回结果的异步任务。异步任务在另一个线程中执行,并且不会阻塞主线程。

9.2.2 处理异步任务的结果

CompletableFuture 提供了thenApply()thenAccept()thenRun()等方法,用于在异步任务完成后对结果进行处理。

示例2:处理异步任务的返回结果
import java.util.concurrent.CompletableFuture;public class CompletableFutureResultExample {public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {System.out.println("Calculating in " + Thread.currentThread().getName());return 42;});future.thenApply(result -> {System.out.println("Result: " + result);return result * 2;}).thenAccept(result -> {System.out.println("Processed Result: " + result);});// 等待所有异步操作完成future.join();}
}

在这个示例中,supplyAsync()创建了一个返回结果的异步任务。然后通过thenApply()对结果进行处理,最后通过thenAccept()消费处理后的结果。

9.3 组合多个CompletableFuture
9.3.1 并行执行任务

CompletableFuture 支持将多个异步任务组合在一起并行执行,并可以使用thenCombine()方法合并任务的结果。

示例3:并行执行和合并结果
import java.util.concurrent.CompletableFuture;public class CompletableFutureCombineExample {public static void main(String[] args) {CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {return 10;});CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {return 20;});CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> {return result1 + result2;});System.out.println("Combined Result: " + combinedFuture.join());}
}

在这个示例中,两个异步任务并行执行,并在完成后通过thenCombine()方法将结果合并。

9.3.2 处理异步任务的依赖关系

在某些情况下,任务B依赖于任务A的结果,CompletableFuture提供了thenCompose()方法来处理这种依赖关系。

示例4:任务依赖关系的处理
import java.util.concurrent.CompletableFuture;public class CompletableFutureComposeExample {public static void main(String[] args) {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "Hello";}).thenCompose(result -> {return CompletableFuture.supplyAsync(() -> {return result + " World";});});System.out.println("Final Result: " + future.join());}
}

在这个示例中,第二个任务依赖于第一个任务的结果,使用thenCompose()方法将它们组合在一起。

9.4 处理异步操作中的异常

CompletableFuture 支持对异步操作中的异常进行处理,可以使用exceptionally()方法来处理异常并提供默认值。

示例5:处理异步任务中的异常
import java.util.concurrent.CompletableFuture;public class CompletableFutureExceptionExample {public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {if (Math.random() > 0.5) {throw new RuntimeException("Something went wrong");}return 42;}).exceptionally(ex -> {System.out.println("Caught exception: " + ex.getMessage());return -1;});System.out.println("Result: " + future.join());}
}

在这个示例中,如果异步任务抛出异常,exceptionally()方法会捕获异常并返回一个默认值。

9.5 CompletableFuture的高级应用
9.5.1 超时处理

CompletableFuture 提供了orTimeout()completeOnTimeout()方法,允许开发者设置超时处理机制。

示例6:处理超时的异步任务
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;public class CompletableFutureTimeoutExample {public static void main(String[] args) {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}return 42;}).orTimeout(2, TimeUnit.SECONDS).exceptionally(ex -> {System.out.println("Task timed out.");return -1;});System.out.println("Result: " + future.join());}
}

在这个示例中,如果异步任务没有在2秒内完成,orTimeout()方法会触发超时处理,并返回默认值。

第九章小结

本章详细介绍了CompletableFuture及其在异步编程中的应用。通过使用CompletableFuture,开发者可以编写非阻塞的异步代码,并处理复杂的任务组合和异常情况。它为现代应用程序的并发处理提供了强大的工具支持。

下一章将探讨JUC中的高级特性,包括自定义同步器(AbstractQueuedSynchronizer,AQS)、ThreadLocal的使用等,帮助开发者掌握更深入的并发编程技巧。
在这里插入图片描述

第十章:JUC高级特性

在前面的章节中,我们讨论了JUC中的各种工具和机制,帮助开发者在并发编程中高效、安全地管理多线程操作。本章将深入探讨JUC中的一些高级特性,包括自定义同步器(AbstractQueuedSynchronizer,AQS)、ThreadLocal的使用等。

10.1 自定义同步器(AQS)
10.1.1 原理

AbstractQueuedSynchronizer(AQS)是JUC中的一个基础框架,用于构建自定义的同步器,如锁、信号量、倒计时器等。AQS基于FIFO队列实现线程的排队和管理,提供了独占模式和共享模式两种模式来支持不同的同步需求。

  • 独占模式:一个线程独占资源,其他线程需要等待。
  • 共享模式:多个线程可以共享资源,通常用于实现信号量。
10.1.2 应用场景

AQS主要用于构建自定义的同步器,如:

  • 实现自定义的锁
  • 创建自定义的同步工具,如自定义的倒计时器或栅栏
  • 实现基于AQS的高级并发控制器
10.1.3 代码实现与示例
示例1:基于AQS实现一个简单的自定义锁
import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class CustomLock {private static class Sync extends AbstractQueuedSynchronizer {@Overrideprotected boolean tryAcquire(int arg) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) {throw new IllegalMonitorStateException();}setExclusiveOwnerThread(null);setState(0);return true;}@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}public boolean isLocked() {return isHeldExclusively();}}private final Sync sync = new Sync();public void lock() {sync.acquire(1);}public void unlock() {sync.release(1);}public boolean isLocked() {return sync.isLocked();}public static void main(String[] args) {CustomLock lock = new CustomLock();Runnable task = () -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + " acquired the lock.");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println(Thread.currentThread().getName() + " released the lock.");}};new Thread(task).start();new Thread(task).start();}
}

在这个示例中,我们使用AQS实现了一个简单的自定义锁,允许多个线程竞争锁,并通过FIFO队列管理线程的等待和调度。

10.2 ThreadLocal
10.2.1 原理

ThreadLocal 是Java中的一个工具类,用于在多线程环境下为每个线程维护独立的变量副本。它通过为每个线程创建一个单独的副本,避免了线程间共享数据导致的并发问题。ThreadLocal通常用于为线程提供独立的上下文信息,如用户会话、事务处理等。

10.2.2 应用场景

ThreadLocal 适用于以下场景:

  • 每个线程需要一个独立的变量副本
  • 需要在线程间隔离共享变量
  • 为每个线程提供独立的上下文信息
10.2.3 代码实现与示例
示例2:使用ThreadLocal管理用户会话
public class ThreadLocalExample {private static final ThreadLocal<String> userSession = ThreadLocal.withInitial(() -> "Default User");public static void main(String[] args) {Runnable task1 = () -> {userSession.set("User1");System.out.println(Thread.currentThread().getName() + " session: " + userSession.get());};Runnable task2 = () -> {userSession.set("User2");System.out.println(Thread.currentThread().getName() + " session: " + userSession.get());};new Thread(task1).start();new Thread(task2).start();}
}

在这个示例中,ThreadLocal为每个线程维护了独立的用户会话信息,避免了线程间数据共享导致的并发问题。

第十章小结

本章探讨了JUC中的一些高级特性,包括基于AQS的自定义同步器和ThreadLocal的使用。通过这些高级特性,开发者可以在更复杂的并发场景中构建自定义的同步工具,并为每个线程提供独立的上下文信息。

至此,这本关于Java工程师面试中JUC相关知识点的书籍已经涵盖了从基础到高级的各种内容,帮助读者全面掌握并发编程的核心知识与技巧。无论是在面试中还是在实际开发中,这些知识都将是你成为优秀Java工程师的重要工具。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vulhub GhostScript 沙箱绕过(CVE-2018-16509)

1.执行以下命令启动靶场环境并在浏览器访问 cd vulhub/ghostscript/CVE-2018-16509 #进入漏洞环境所在目录 docker-compose up -d #启动靶场 docker ps #查看容器信息 2.访问网页 3.下载包含payload的png文件 vulhub/ghostscript/CVE-2018-16509/poc.png at master vulh…

java宠物商城网站系统的设计与实现

springboot508基于Springboot宠物商城网站系统 题目&#xff1a;宠物商城网站系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往…

Datawhale X 李宏毅苹果书 AI夏令营第五期 DL进阶方向 Task3笔记

Datawhale X 李宏毅苹果书 向李宏毅学深度学习&#xff08;进阶&#xff09; 是 Datawhale 2024 年 AI 夏令营第五期的学习活动&#xff08;“深度学习 进阶”方向&#xff09; 往期task1链接&#xff1a;深度学习进阶-Task1 往期task2链接&#xff1a;深度学习进阶-Task2 我做…

如何利用评论进行有效的 ASO

如何利用评论进行有效的ASO的问题的答案通常以“正面评论”一词开始。确实&#xff0c;这句话首先浮现在脑海中。但这个问题的答案包括负面评论、用户体验、提高知名度、评分、根据评论优化应用程序以及许多其他有趣的点。这里几乎没有无聊的统计数据&#xff0c;这些数字也不会…

c++ 原型模式

文章目录 什么是原型模式为什么要使用原型模式使用场景示例 什么是原型模式 用原型实例指定创建对象的种类&#xff0c;并通过拷贝这些原型创建新的对象&#xff0c;简单理解就是“克隆指定对象” 为什么要使用原型模式 原型模式&#xff08;Prototype Pattern&#xff09;是…

绝对定位导致内容自动换行问题解决

今天在做一个定位元素的时候遇到一个嵌套定位之后&#xff0c;使用绝对定位的元素的内容自动换行的问题&#xff0c;希望不换行只在一行显示。 可以通过添加 white-space: nowrap; 样式控制不换行 <div class"box"><div class"box1"><div …

【MySQL超详细安装步骤】Centos7安装MySQL8

文章目录 1.卸载2.修改yum源为阿里源2.1首先检查是否安装wget2.2 备份 yum 源文件2.3 下载阿里云yum源文件2.4 清理yum缓存 3.安装mysql源3.1 下载mysql源3.2 安装mysql源3.3 检查是否安装成功 4. 安装MySQL4.1 使用yum安装4.2 启动MySQL 5.配置防火墙5.1 开放3306端口 6.登录M…

2. GIS数据工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…

macos系统内置php文件列表 系统自带php卸载方法

在macos系统中, 自带已经安装了php, 根据不同的macos版本php的版本号可能不同, 我们可以通过 which php 命令来查看mac自带的默认php安装路径, 不过注意这个只是php的执行文件路径. 系统自带php文件列表 一下就是macos默认安装的php文件列表. macos 10.15内置PHP文件列表配置…

手机扬声器音量总是不够大?试试“扬声器助推器”吧

手机的扬声器音量总是不够大&#xff0c;尤其是在嘈杂的环境中&#xff0c;音乐和视频的声音总是不太清晰。直到我发现了这款“扬声器助推器”&#xff0c;我的手机音质瞬间提升了好几个档次。 软件简介&#xff1a; “扬声器助推器”利用先进的音频处理技术&#xff0c;能够…

卷积神经网络(一)

目录 一.卷积神经网络的组成 二.卷积层 目的: 参数: 计算公式 卷积运算过程 三.padding-零填充 1.Valid and Same卷积 2.奇数维度的过滤器 四.stride步长 五.多通道卷积 1.多卷积核(多个Filter) 六.卷积总结 七.池化层(Pooling) 八.全连接层 都看到这里了,点个…

C++——STL(vector类)

1.vector的介绍 vector的文档介绍 vector是表示可变大小数组的序列容器。vector就像数组一样&#xff0c;拥有连续的储存空间来储存元素&#xff0c;这就意味着可以通过下标的方式来访问vector的元素&#xff0c;很高效&#xff1b;但与数组不同的是vector的大小是动态改变的…

新加坡服务器:亚洲地区的优选之选

在全球化日益加深的今天&#xff0c;互联网成为了连接世界的桥梁&#xff0c;而服务器作为互联网的基础设施&#xff0c;其选择对于企业的业务发展和用户体验至关重要。在众多海外服务器中&#xff0c;新加坡服务器以其独特的地理位置、先进的技术支持、稳定的网络环境以及完善…

站长工具 API 接口,助力网站管理新高度

站长工具是一款非常实用的网站管理工具&#xff0c;通过其提供的API接口&#xff0c;可以轻松实现多种功能&#xff0c;如域名反查、域名备案查询、IPV6归属地查询等。这些功能可以帮助网站管理员更好地管理和优化自己的网站。以下是简单的代码示例&#xff0c;展示了如何使用站…

未雨绸缪:环保专包二级资质续期工程师招聘时间策略

对于环保企业而言&#xff0c;在二级资质续期前启动工程师招聘的时间规划至关重要。考虑到招聘流程的复杂性、企业内部需求的变化以及政策标准的更新&#xff0c;建议环保企业在二级资质续期前至少提前6至12个月启动工程师招聘工作。这个时间规划可以细化为以下几个阶段&#x…

SprinBoot+Vue个性化旅游推荐系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

C语言深度剖析--不定期更新的第三弹

hello,everybody!最近又更新了哈&#xff0c;希望对大家有所帮助 switch case关键字 基本语法&#xff1a; switch(整型变量/常量/整型表达式){case 1:var 1;break;case 2:var 2;break;case 3:var 3;break;default:break; }switch case语句也和if语句一样具有判断和分支的功…

挂轨巡检机器人在发电厂与煤矿皮带机场景的应用

一、引言 在发电厂和煤矿等重工业领域&#xff0c;皮带机作为关键设备&#xff0c;其运行状态直接关系到生产效率和安全。然而&#xff0c;传统的人工巡检方式不仅效率低下&#xff0c;还存在安全隐患。随着智能巡检技术的不断发展&#xff0c;杭州旗晟智能科技有限公司推出的…

Git学习尚硅谷(003 git分支操作)

尚硅谷Git入门到精通全套教程&#xff08;涵盖GitHub\Gitee码云\GitLab&#xff09; 总时长 4:52:00 共45P 此文章包含第15p-第p18的内容 文章目录 git分支操作分支介绍分支的好处 分支的操作查看分支创建分支切换分支&修改分支合并分支正常合并分支冲突合并 git分支操作…

如何打造在线音乐网站?java springboot架构,vue前端开发,音乐分享新体验

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…