Java并发编程:从基础到高级实战
在现代软件开发中,并发编程已成为不可或缺的核心技能。随着多核处理器的普及和分布式系统的发展,能否编写高效、线程安全的并发程序直接决定了应用程序的性能和可靠性。Java作为一门成熟的企业级编程语言,提供了丰富的并发编程工具和API,但同时也带来了复杂性和各种潜在的陷阱。
本文将系统性地介绍Java并发编程的各个方面,从基本概念到高级实践,从底层原理到实际应用,帮助开发者全面掌握并发编程技能。我们将通过大量实际代码示例和详细注释,展示如何避免常见的并发问题,构建高性能、线程安全的应用程序。
1.并发编程的基本概念
1.1.线程与进程的区别
- 进程:
进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。比如我们打开windows的任务管理器可以看到有多少进行在我们计算机上运行。
进程的特性:
- 独立性:进程之间相互隔离,一个进程崩溃通常不会影响其他进程
- 资源开销:创建和销毁进程开销较大,因为需要分配和回收系统资源
- 通信复杂:进程间通信(IPC)需要特定机制,如管道、信号、共享内存等
- 线程:
线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。Java程序天生是读线程的程序,哪怕启用一个main方法。
线程的特性:
- 共享资源:线程可以访问所属进程的所有资源
- 轻量级:创建和销毁线程的开销远小于进程
- 通信简单:线程间可以通过共享内存直接通信
我们可以通过ThreadMXBean中的dumpAllThreads方法返来看一个Java程序都有哪些线程:
// 执行main方法
public static void main(String[] args) {ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);for(ThreadInfo threadInfo : threadInfos){System.out.println("Thread Id:" + threadInfo.getThreadId() + " Thread Name :[" + threadInfo.getThreadName() + "]");}}// 控制台打印结果
Thread Id:6 Thread Name :[Monitor Ctrl-Break]
Thread Id:5 Thread Name :[Attach Listener]
Thread Id:4 Thread Name :[Signal Dispatcher]
Thread Id:3 Thread Name :[Finalizer]
Thread Id:2 Thread Name :[Reference Handler]
Thread Id:1 Thread Name :[main]Process finished with exit code 0
可以看出执行一个mian方法是由多个线程同时执行的。
1.2.并发与并行的区别
并发(Concurrency)和并行(Parallelism)**是经常被混淆的两个概念,它们在Java并发编程中有着明确的区别:
- 并发:
并发是指系统具有处理多个任务的能力,这些任务在时间上是重叠的,但不一定是同时执行的。在单核CPU上通过时间片轮转实现的多线程就是典型的并发。并发强调的是任务的组织和结构,是一种逻辑上的"同时"。
- 并行:
并行是指系统具有同时执行多个任务的能力,这需要多核或多处理器的硬件支持。并行关注的是任务的执行和性能提升,是一种物理上的真正同时。比如4C8G能做到并行处理4个任务。
Rob Pike的经典描述很好地阐释了两者的区别:
并发是同一时间应对(dealing with)多件事情的能力
并行是同一时间动手做(doing)多件事情的能力
举例来说:
并发:一个服务员同时照看多张餐桌,通过快速切换服务实现"同时"服务的效果
并行:多个服务员各自独立照看不同的餐桌,真正同时提供服务
在Java中,我们通过Thread类、synchronized等实现并发控制,而通过ForkJoinPool、parallelStream()等API实现并行计算。现代高并发系统通常需要同时考虑这两个方面:良好的并发设计保证程序正确性,合理的并行执行提高系统吞吐量。
1.3.Java中的线程模型
线程模型是用户线程和内核线程之间的关联方式,常见的线程模型有这三种:
- 一对一(一个用户线程对应一个内核线程)
- 多对一(多个用户线程映射到一个内核线程)
- 多对多(多个用户线程映射到多个内核线程)
为了帮助我们的理解,我们讲以上三种线程模型具象化:
Java线程模型经历了多次演进,目前采用的是基于操作系统原生线程的一对一模型。这意味着每个Java线程都直接对应一个操作系统线程,由操作系统内核负责线程调度和管理。Java线程的生命周期包括以下状态:
- 新建(NEW):线程对象刚被创建,但尚未启动。
- 可运行(RUNNABLE):线程正在JVM中执行或等待操作系统资源。
- 阻塞(BLOCKED):线程等待获取监视器锁以进入同步块/方法。
- 等待(WAITING):线程无限期等待其他线程执行特定操作。
- 超时等待(TIMED_WAITING):线程在指定时间内等待。
- 终止(TERMINATED):线程执行完毕或异常退出。
为了方便理解,列出JVM进程内部和线程的模型:
2.线程安全问题
- 竞态条件
指程序的正确性依赖于线程执行时序的情况。当多个线程访问共享资源且至少有一个线程修改该资源时,如果缺乏适当的同步,就可能出现竞态条件。
- 数据竞争
这个是竞态条件的一种特殊形式,发生在两个或多个线程同时访问同一内存位置,且至少有一个是写操作,且这些操作没有正确的同步。
- 死锁、活锁与饥饿
死锁(Deadlock)是指两个或多个线程互相持有对方需要的资源,导致所有线程都无法继续执行的状态。死锁发生的四个必要条件:
- 互斥条件:资源一次只能被一个线程占用
- 占有并等待:线程持有至少一个资源,同时等待获取其他线程占用的资源
- 不可抢占:已分配给线程的资源不能被其他线程强行夺取
- 循环等待:存在一个线程的循环等待链
3.Java中的并发工具
synchronized
关键字
synchronized是Java中最基本的同步机制,它可以确保多个线程不会同时执行某个代码块或方法。
synchronized
提供了原子性和可见性保证,即被synchronized
保护的代码块在同一时间只能有一个线程执行,且线程在退出同步块时对共享变量的修改会立即对其他线程可见。
synchronized实例方法使用对象实例作为锁,静态方法使用类的Class对象作为锁,而同步代码块可以指定任意对象作为锁,synchronized的三种使用方式:
- 实例方法同步
- 静态方法同步
- 同步代码块
public class SynchronizedExample {private int count = 0;private static int staticCount = 0;private final Object lock = new Object();// 1. 实例方法同步public synchronized void increment() {count++;}// 2. 静态方法同步public static synchronized void staticIncrement() {staticCount++;}// 3. 同步代码块public void incrementWithBlock() {// 一些非同步代码synchronized (lock) { // 使用特定对象作为锁count++;}// 更多非同步代码}public static void main(String[] args) throws InterruptedException {SynchronizedExample example = new SynchronizedExample();Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();SynchronizedExample.staticIncrement();example.incrementWithBlock();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();SynchronizedExample.staticIncrement();example.incrementWithBlock();}});t1.start();t2.start();t1.join();t2.join();System.out.println("Instance count: " + example.count); // 2000System.out.println("Static count: " + staticCount); // 2000}
}
volatile
关键字
volatile
是比synchronized
更轻量级的同步机制,它确保变量的可见性和有序性,但不保证原子性。当一个字段被声明为volatile
时:
- 任何线程对该变量的修改都会立即写入主内存
- 任何线程读取该变量时都会直接从主内存读取,而不是使用缓存
- 禁止指令重排序优化
public class VolatileExample {private volatile boolean flag = false;public void writer() {flag = true; // 写volatile变量}public void reader() {if (flag) { // 读volatile变量System.out.println("Flag is true");}}public static void main(String[] args) {VolatileExample example = new VolatileExample();Thread writer = new Thread(example::writer);Thread reader = new Thread(example::reader);writer.start();reader.start();}
}
ReentrantLock
与ReadWriteLock
ReentrantLock
是java.util.concurrent.locks
包中的可重入互斥锁,它提供了比synchronized
更灵活的锁操作:
- 可中断的锁获取
- 超时获取锁
- 公平锁与非公平锁选择
- 多个条件变量
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock(); // 获取锁try {count++;} finally {lock.unlock(); // 确保锁被释放}}public static void main(String[] args) throws InterruptedException {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();t1.join();t2.join();System.out.println("Final count: " + example.count); // 2000}
}
ReadWriteLock
接口及其实现ReentrantReadWriteLock
实现了读写分离的锁策略,适用于读多写少的场景:
- 读锁是共享的,多个线程可以同时持有读锁
- 写锁是独占的,同一时间只能有一个线程持有写锁
- 写锁可以降级为读锁,但读锁不能升级为写锁
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReadWriteLock rwLock = new ReentrantReadWriteLock();private String data = "Initial Data";public String readData() {rwLock.readLock().lock();try {return data;} finally {rwLock.readLock().unlock();}}public void writeData(String newData) {rwLock.writeLock().lock();try {data = newData;} finally {rwLock.writeLock().unlock();}}// 锁降级示例public void lockDowngrade(String newData) {rwLock.writeLock().lock();try {// 独占写权限data = newData;// 在释放写锁前获取读锁rwLock.readLock().lock();} finally {rwLock.writeLock().unlock(); // 释放写锁,降级为读锁}try {System.out.println("Data after downgrade: " + data);} finally {rwLock.readLock().unlock();}}
}
-
java.util.concurrent
包中的工具
java.util.concurrent
包提供了丰富的并发工具类,可以大致分为以下几类:
- 原子变量类:如
AtomicInteger
、AtomicLong
等,提供原子操作- 锁和条件:如
ReentrantLock
、Condition
等- 并发集合:如
ConcurrentHashMap
、CopyOnWriteArrayList
等- 线程池:如
ExecutorService
、ThreadPoolExecutor
等- 同步辅助类:如
CountDownLatch
、CyclicBarrier
、Semaphore
等
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;public class ConcurrentToolsExample {private static final int THREAD_COUNT = 10;private static final CountDownLatch startLatch = new CountDownLatch(1);private static final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT);private static final AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);for (int i = 0; i < THREAD_COUNT; i++) {executor.execute(() -> {try {startLatch.await(); // 等待主线程发令// 模拟工作Thread.sleep((long) (Math.random() * 1000));counter.incrementAndGet();endLatch.countDown(); // 完成工作} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}System.out.println("Starting workers...");startLatch.countDown(); // 发令开始endLatch.await(); // 等待所有工作完成System.out.println("All workers completed. Counter: " + counter.get());executor.shutdown();}
}
4.线程池与任务调度
Executor
框架
Java的Executor框架将任务提交与任务执行解耦,提供了线程池管理的标准化方法。使用线程池可以避免频繁创建和销毁线程的开销,提高系统性能。
Executors
提供了多种预配置的线程池。框架的核心接口包括:
Executor
:简单的执行接口ExecutorService
:扩展了Executor,添加了生命周期管理方法ScheduledExecutorService
:支持延迟和定期任务ThreadPoolExecutor
:可配置的线程池实现Executors
:工厂类,提供创建线程池的便捷方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class ExecutorFrameworkExample {public static void main(String[] args) {// 创建固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(5);// 提交10个任务for (int i = 0; i < 10; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Executing task " + taskId + " on " + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 优雅关闭线程池executor.shutdown();try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();}}
}
ThreadPoolExecutor
的使用与配置
ThreadPoolExecutor
是线程池的核心实现类,提供了丰富的配置选项:
- 核心线程数(corePoolSize)
- 最大线程数(maximumPoolSize)
- 线程空闲时间(keepAliveTime)
- 工作队列(workQueue)
- 线程工厂(threadFactory)
- 拒绝策略(handler)
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolConfigExample {public static void main(String[] args) {// 创建自定义线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(2, // 核心线程数4, // 最大线程数60, // 空闲线程存活时间TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), // 工作队列容量new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);// 提交任务for (int i = 0; i < 10; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Task " + taskId + " executed by " + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 监控线程池状态monitorThreadPool(executor);// 关闭线程池executor.shutdown();}private static void monitorThreadPool(ThreadPoolExecutor executor) {new Thread(() -> {while (!executor.isTerminated()) {System.out.println("Pool size: " + executor.getPoolSize());System.out.println("Active threads: " + executor.getActiveCount());System.out.println("Completed tasks: " + executor.getCompletedTaskCount());System.out.println("Queue size: " + executor.getQueue().size());try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}).start();}
}
-
ScheduledExecutorService
的定时任务调度
ScheduledExecutorService
接口扩展了ExecutorService
,支持延迟和周期性任务执行。scheduleAtFixedRate
以固定频率执行任务,而scheduleWithFixedDelay
保证任务执行完成后的固定延迟。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledTaskExample {public static void main(String[] args) throws InterruptedException {ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);// 延迟执行scheduler.schedule(() -> {System.out.println("Delayed task executed after 2 seconds");}, 2, TimeUnit.SECONDS);// 固定速率执行(不考虑任务执行时间)scheduler.scheduleAtFixedRate(() -> {System.out.println("Fixed rate task executed at " + System.currentTimeMillis());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {Thread.currentThread().interrupt();}}, 0, 2, TimeUnit.SECONDS);// 固定延迟执行(考虑任务执行时间)scheduler.scheduleWithFixedDelay(() -> {System.out.println("Fixed delay task executed at " + System.currentTimeMillis());try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {Thread.currentThread().interrupt();}}, 0, 2, TimeUnit.SECONDS);// 运行一段时间后关闭Thread.sleep(10000);scheduler.shutdown();}
}
5.并发集合类
ConcurrentHashMap
ConcurrentHashMap
是线程安全的哈希表实现,通过分段锁或CAS+synchronized(Java 8+)实现高并发访问。
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();// 并发安全的putIfAbsentmap.putIfAbsent("key1", 1);// 原子性更新map.compute("key1", (k, v) -> v == null ? 1 : v + 1);// 搜索String result = map.search(1, (k, v) -> v > 0 ? "found" : null);System.out.println("Search result: " + result);// 遍历map.forEach(2, (k, v) -> System.out.println("Key: " + k + " Value: " + v + " on " + Thread.currentThread().getName()));// 归约int sum = map.reduceValues(2, Integer::sum);System.out.println("Sum of values: " + sum);}
}
CopyOnWriteArrayList
CopyOnWriteArrayList
是线程安全的List
实现,通过在修改时创建底层数组的新副本来实现并发安全,适合读多写少的场景。由于写操作需要复制整个数组,CopyOnWriteArrayList
不适合写操作频繁的场景。
import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteArrayListExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();// 添加元素(创建新数组)list.add("Item1");list.add("Item2");// 迭代器使用快照,不受后续修改影响new Thread(() -> {list.forEach(item -> {System.out.println("Iterating: " + item);try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}).start();// 修改不影响正在进行的迭代list.add("Item3");list.remove("Item1");}
}
BlockingQueue
及其实现类
BlockingQueue
是支持阻塞操作的队列接口,常用于生产者-消费者模式。BlockingQueue
提供了多种插入和移除方法,包括立即返回、抛出异常、阻塞等待和超时等待等不同行为。常见实现包括:
ArrayBlockingQueue
:有界数组实现LinkedBlockingQueue
:可选有界链表实现PriorityBlockingQueue
:优先级排序的无界队列SynchronousQueue
:不存储元素的特殊队列DelayQueue
:元素延迟出队的无界队列
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class BlockingQueueExample {public static void main(String[] args) {BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);// 生产者new Thread(() -> {try {queue.put("Item1");queue.put("Item2");boolean offered = queue.offer("Item3", 1, TimeUnit.SECONDS);System.out.println("Item3 offered: " + offered);offered = queue.offer("Item4", 1, TimeUnit.SECONDS); // 等待1秒后失败System.out.println("Item4 offered: " + offered);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}).start();// 消费者new Thread(() -> {try {Thread.sleep(1500); // 让生产者先填充队列System.out.println("Polled: " + queue.poll());System.out.println("Polled: " + queue.poll());System.out.println("Polled: " + queue.poll());String item = queue.poll(2, TimeUnit.SECONDS); // 等待2秒System.out.println("Polled after wait: " + item);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}).start();}
}
6.原子操作与CAS
AtomicInteger
、AtomicLong
等原子类
java.util.concurrent.atomic
包提供了一系列原子变量类,如AtomicInteger
、AtomicLong
、AtomicBoolean
等,它们通过CAS(Compare-And-Swap)指令实现原子操作。AtomicInteger
适合低并发下的原子计数,而LongAdder
在高并发下性能更好,但读取结果时可能需要合并多个cell的值。
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;public class AtomicExample {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInt = new AtomicInteger(0);// 并发递增Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {atomicInt.incrementAndGet();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {atomicInt.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println("AtomicInteger result: " + atomicInt.get()); // 2000// LongAdder在高并发下性能更好LongAdder longAdder = new LongAdder();Thread t3 = new Thread(() -> {for (int i = 0; i < 1000; i++) {longAdder.increment();}});Thread t4 = new Thread(() -> {for (int i = 0; i < 1000; i++) {longAdder.increment();}});t3.start();t4.start();t3.join();t4.join();System.out.println("LongAdder result: " + longAdder.sum()); // 2000}
}
- CAS(Compare-And-Swap)机制
CAS是一种无锁编程技术,包含三个操作数:内存位置(V)、预期原值(A)和新值(B)。当且仅当V的值等于A时,CAS才会将V的值设为B,否则不做任何操作。CAS是实现非阻塞算法的基础,但存在ABA问题(即值从A变为B又变回A,CAS无法感知中间变化)。
import java.util.concurrent.atomic.AtomicReference;public class CASExample {public static class OptimisticLock {private AtomicReference<Object> lockRef = new AtomicReference<>(new Object());public boolean tryLock() {Object current = lockRef.get();return current != null && lockRef.compareAndSet(current, null);}public void unlock() {lockRef.compareAndSet(null, new Object());}}public static void main(String[] args) {OptimisticLock lock = new OptimisticLock();if (lock.tryLock()) {try {System.out.println("Lock acquired, doing work...");} finally {lock.unlock();}} else {System.out.println("Failed to acquire lock");}}
}
-
AtomicReference
与AtomicStampedReference
AtomicReference
提供对对象引用的原子操作,而AtomicStampedReference
通过添加版本戳解决ABA问题。AtomicStampedReference
通过维护一个版本戳,可以检测到对象引用是否被修改过,即使当前值与预期值相同。
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;public class AtomicReferenceExample {public static void main(String[] args) {// AtomicReference示例AtomicReference<String> atomicRef = new AtomicReference<>("initial");boolean changed = atomicRef.compareAndSet("initial", "updated");System.out.println("Changed: " + changed + ", value: " + atomicRef.get());// AtomicStampedReference示例(解决ABA问题)AtomicStampedReference<String> stampedRef = new AtomicStampedReference<>("initial", 0);int[] stampHolder = new int[1];String current = stampedRef.get(stampHolder);System.out.println("Current value: " + current + ", stamp: " + stampHolder[0]);// 第一次更新stampedRef.compareAndSet("initial", "updated", stampHolder[0], stampHolder[0] + 1);// 模拟ABA问题stampedRef.compareAndSet("updated", "initial", 1, 2);// 使用stamp检测中间变化boolean success = stampedRef.compareAndSet("initial", "updated", 0, 1);System.out.println("With wrong stamp, update success: " + success);success = stampedRef.compareAndSet("initial", "updated", 2, 3);System.out.println("With correct stamp, update success: " + success);}
}
7.并发编程中的性能问题
7.1.上下文切换的开销
上下文切换是多线程编程中不可避免的性能开销。当CPU从一个线程切换到另一个线程时,需要保存当前线程的状态并恢复另一个线程的状态。这种切换操作通常需要消耗1-10微秒的时间,在高并发场景下会显著影响性能。
减少上下文切换的实用技巧:
- 合理设置线程池大小(CPU密集型任务建议N+1,IO密集型建议2N+1)
- 使用协程(Java 19+的虚拟线程)
- 避免不必要的线程唤醒
- 使用无锁数据结构
// 上下文切换性能测试
public class ContextSwitchBenchmark {private static final int ITERATIONS = 1000000;private static final Object lock = new Object();private static volatile boolean flag = true;public static void main(String[] args) throws InterruptedException {// 测试线程切换开销Thread t1 = new Thread(() -> {while (flag) {synchronized (lock) {// 空循环,仅用于触发上下文切换}}});Thread t2 = new Thread(() -> {while (flag) {synchronized (lock) {// 空循环,仅用于触发上下文切换}}});long start = System.nanoTime();t1.start();t2.start();Thread.sleep(1000); // 运行1秒钟flag = false;long duration = System.nanoTime() - start;System.out.printf("Context switches per second: %,d%n", ITERATIONS * 1000000000L / duration);}
}
7.2. 锁的粒度与性能
锁的粒度选择直接影响并发程序的性能。以下是三种常见的锁策略:
1.粗粒度锁:简单但并发性低
// 粗粒度锁示例
public class CoarseLockExample {private final List<Integer> list = new ArrayList<>();private final Object lock = new Object();public void add(int value) {synchronized (lock) {list.add(value);}}public boolean contains(int value) {synchronized (lock) {return list.contains(value);}}
}
2. 细粒度锁:复杂但并发性高
// 细粒度锁示例(分段锁)
public class FineGrainedLockExample {private final List<Integer>[] segments;private final Object[] locks;private static final int SEGMENT_COUNT = 16;public FineGrainedLockExample() {segments = new List[SEGMENT_COUNT];locks = new Object[SEGMENT_COUNT];for (int i = 0; i < SEGMENT_COUNT; i++) {segments[i] = new ArrayList<>();locks[i] = new Object();}}public void add(int value) {int segment = value % SEGMENT_COUNT;synchronized (locks[segment]) {segments[segment].add(value);}}public boolean contains(int value) {int segment = value % SEGMENT_COUNT;synchronized (locks[segment]) {return segments[segment].contains(value);}}
}
3.无锁编程:最高并发性但实现复杂
// 无锁计数器示例
public class LockFreeCounter {private final AtomicInteger counter = new AtomicInteger();public void increment() {int oldValue;int newValue;do {oldValue = counter.get();newValue = oldValue + 1;} while (!counter.compareAndSet(oldValue, newValue));}public int get() {return counter.get();}
}
7.3.无锁编程与乐观锁
无锁编程通过CAS(Compare-And-Swap)操作实现线程安全,避免了传统锁带来的性能问题。
// 无锁栈实现
public class LockFreeStack<T> {private static class Node<T> {final T value;Node<T> next;Node(T value) {this.value = value;}}private AtomicReference<Node<T>> top = new AtomicReference<>();public void push(T value) {Node<T> newHead = new Node<>(value);Node<T> oldHead;do {oldHead = top.get();newHead.next = oldHead;} while (!top.compareAndSet(oldHead, newHead));}public T pop() {Node<T> oldHead;Node<T> newHead;do {oldHead = top.get();if (oldHead == null) {return null;}newHead = oldHead.next;} while (!top.compareAndSet(oldHead, newHead));return oldHead.value;}
}
8.并发编程的最佳实践
8.1. 避免过度同步
过度同步会降低程序性能并可能导致死锁。只在必要时同步,并尽量缩小同步范围。
// 避免过度同步的示例
public class OptimizedSynchronization {private final Map<String, String> cache = new HashMap<>();private final Object lock = new Object();// 不推荐的过度同步方式public String getValueSlow(String key) {synchronized (lock) {// 模拟耗时操作try { Thread.sleep(10); } catch (InterruptedException e) {}return cache.get(key);}}// 优化后的同步方式public String getValueFast(String key) {// 先尝试无锁读取String value = cache.get(key);if (value == null) {synchronized (lock) {// 双重检查value = cache.get(key);if (value == null) {// 模拟耗时操作try { Thread.sleep(10); } catch (InterruptedException e) {}value = "generated-value";cache.put(key, value);}}}return value;}
}
8.2.使用不可变对象
不可变对象天生线程安全,可以避免同步问题。
// 不可变对象示例
public final class ImmutablePerson {private final String name;private final int age;private final List<String> hobbies;public ImmutablePerson(String name, int age, List<String> hobbies) {this.name = name;this.age = age;this.hobbies = Collections.unmodifiableList(new ArrayList<>(hobbies));}public String getName() { return name; }public int getAge() { return age; }public List<String> getHobbies() { return hobbies; }
}
8.3.合理使用线程池
正确配置线程池对系统性能至关重要。
// 线程池最佳实践
public class ThreadPoolBestPractice {public static void main(String[] args) {// 根据任务类型选择合适的线程池ExecutorService executor = new ThreadPoolExecutor(4, // 核心线程数8, // 最大线程数60, // 空闲线程存活时间TimeUnit.SECONDS,new ArrayBlockingQueue<>(100), // 有界队列防止资源耗尽new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略);// 提交任务for (int i = 0; i < 100; i++) {final int taskId = i;executor.execute(() -> {System.out.println("Executing task " + taskId + " on " + Thread.currentThread().getName());try {Thread.sleep(100); // 模拟任务执行} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 优雅关闭executor.shutdown();try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();Thread.currentThread().interrupt();}}
}
8.4.使用并发工具类替代手动同步
Java并发包提供了许多高级工具类,可以替代手动同步。
// 使用CountDownLatch协调多个线程
public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threadCount = 5;CountDownLatch startLatch = new CountDownLatch(1);CountDownLatch doneLatch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {new Thread(() -> {try {startLatch.await(); // 等待开始信号System.out.println(Thread.currentThread().getName() + " is working");Thread.sleep((long) (Math.random() * 1000));doneLatch.countDown(); // 完成工作} catch (InterruptedException e) {Thread.currentThread().interrupt();}}).start();}System.out.println("Preparing...");Thread.sleep(1000); // 模拟准备工作System.out.println("Ready, go!");startLatch.countDown(); // 发令开始doneLatch.await(); // 等待所有线程完成System.out.println("All workers completed");}
}
9.调试与测试并发程序
9.1.使用Thread.dumpStack()
进行调试
// 线程堆栈调试示例
public class ThreadDebugExample {public static void main(String[] args) {new Thread(() -> {System.out.println("Thread started");Thread.dumpStack(); // 打印当前线程堆栈doWork();}).start();}private static void doWork() {System.out.println("Doing work...");// 模拟工作try { Thread.sleep(1000); } catch (InterruptedException e) {}}
}
9.2.使用jstack
分析线程状态
jstack是Java虚拟机自带的线程堆栈分析工具,能够帮助开发者诊断线程死锁、阻塞、高CPU占用等问题。jstack输出的线程状态主要有以下几种
- RUNNABLE:线程正在执行或准备执行,等待CPU调度
- BLOCKED:线程被阻塞,等待获取监视器锁(monitor lock)
- WAITING:无限期等待其他线程执行特定操作
- TIMED_WAITING:有时限的等待状态
- TERMINATED:线程已终止
以下是使用jstack分析线程状态的详细步骤和实用命令和基本使用步骤:
1. 定位Java进程ID
首先需要确定要分析的Java进程ID(PID),可以使用以下命令:
jps -l # 列出所有Java进程及其主类名
# 或
ps -ef | grep java # 在Linux/Unix系统上查找Java进程
2. 查看线程CPU使用情况
使用top命令查看进程和线程的CPU使用情况:
top # 查看所有进程的CPU使用情况
top -Hp [PID] # 查看指定Java进程下各线程的CPU使用情况
3. 转换线程ID为16进制
jstack输出的线程ID(nid)是16进制格式,而top命令显示的是10进制,需要转换:
printf "%x\n" [十进制线程ID] # 将top显示的线程ID转换为16进制
4. 获取线程堆栈信息
使用jstack命令获取线程堆栈:
jstack [PID] > thread_dump.txt # 将堆栈信息输出到文件
5. 分析特定线程
在thread_dump.txt中搜索转换后的16进制线程ID(nid),找到对应的线程堆栈信息。
例如 :
1. 诊断CPU占用过高问题
- 使用
top
找到CPU占用高的Java进程- 使用
top -Hp [PID]
找到该进程中CPU占用高的线程- 将线程ID转换为16进制
- 使用jstack获取线程转储并查找对应线程
- 分析该线程的堆栈信息,定位问题代码
2. 诊断死锁问题
- 使用
jstack -l [PID]
获取线程转储- 搜索输出中的"deadlock"关键词
- 分析相互等待锁的线程链
- 查看每个线程持有的锁和等待的锁
死锁实例输出:
"Thread-1" prio=6 tid=0x02bcf000 nid=0xc70 waiting for monitor entry [0x02f6f000]java.lang.Thread.State: BLOCKED (on object monitor)at com.demo.DeadLock$2.run(DeadLock.java:40)- waiting to lock <0x22a297a8> (a java.lang.Object)- locked <0x22a297b0> (a java.lang.Object)"Thread-0" prio=6 tid=0x02bce400 nid=0xba0 waiting for monitor entry [0x02f1f000]java.lang.Thread.State: BLOCKED (on object monitor)at com.demo.DeadLock$1.run(DeadLock.java:25)- waiting to lock <0x22a297b0> (a java.lang.Object)- locked <0x22a297a8> (a java.lang.Object)
- 查找状态为Blocked的线程
- 查看
waiting to lock <...>
信息,确定线程在等待哪个锁 - 查找持有该锁的线程
10.总结
Java并发编程既强大又复杂。通过本文我们了解了:
-
并发编程的复杂性:线程安全、竞态条件、死锁等问题
-
如何选择合适的并发工具:
- 基本同步:synchronized, volatile
- 高级工具:ReentrantLock, ReadWriteLock
- 并发集合:ConcurrentHashMap, CopyOnWriteArrayList
- 线程池:ExecutorService, ThreadPoolExecutor
- 原子变量:AtomicInteger, LongAdder
-
持续学习与实践的重要性:
- 关注Java新版本中的并发特性(如虚拟线程)
- 学习响应式编程等新范式
- 在实际项目中应用并发模式
// 综合示例:高性能缓存实现
public class ConcurrentCache<K, V> {private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>();private final ConcurrentHashMap<K, FutureTask<V>> tasks = new ConcurrentHashMap<>();private final Executor executor = ForkJoinPool.commonPool();public V get(K key, Callable<V> loader) throws Exception {while (true) {FutureTask<V> task = tasks.get(key);if (task != null) {try {return task.get();} catch (ExecutionException e) {tasks.remove(key, task);throw (Exception) e.getCause();} catch (CancellationException e) {tasks.remove(key, task);}}FutureTask<V> newTask = new FutureTask<>(loader);task = tasks.putIfAbsent(key, newTask);if (task == null) {task = newTask;executor.execute(task);}}}
}
并发编程是一门需要不断实践和学习的艺术。从简单的锁开始,逐步掌握更高级的并发工具和模式,最终写出既正确又高效的并发代码。文章写的仓促,排版格式不好请大家见谅,感谢大家查阅!
相关文章:
Java并发编程:从基础到高级实战
在现代软件开发中,并发编程已成为不可或缺的核心技能。随着多核处理器的普及和分布式系统的发展,能否编写高效、线程安全的并发程序直接决定了应用程序的性能和可靠性。Java作为一门成熟的企业级编程语言,提供了丰富的并发编程工具和API&…...
在 Excel 中使用东方仙盟软件————仙盟创梦IDE
安装插件 用仙盟创梦编写插件代码 源码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ExcelDna.Integration;namespace 东方仙盟.仙盟创梦IDE_招标系统 {public static class 仙盟创梦_招标专…...
win7无线网络名称显示为编码,连接对应网络不方便【解决办法】
使用多个网络时可能需要切换到打印机的网络来打印东西,但是win7的编码问题导致不知道哪个网络是对应网络,可以使用批处理命令来自动连接道指定网络 将这个代码用文本文件打开后粘贴,然后另存为ansi格式的bat文件 代码中使用两种方式进行连接…...
.NET 10 - 尝试一下Minimal Api的Validation新特性
1.简单介绍 2025年11月微软将会发布.NET10,这是LTS(Long Term Support)版本。当前.NET10已经处于Preview4版本,微软对Runtime, Library, SDK, C#, Asp.NET Core, MAUI等都做了很多enhancement。近些年微软对Minimal Api一直在持续地更新。在.NET8中, Mi…...
C# Task 与 SynchronizationContext
示例代码 using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks;namespace Test {internal class Program{static void Main(string[] args){_mainThreadSynchronizationContext new ThreadSynchronizationContext(&qu…...
iOS Runtime与RunLoop的对比和使用
Runtime 机制 核心概念 Objective-C 的动态特性:Objective-C 是一门动态语言,很多工作都是在运行时而非编译时决定的消息传递机制:方法调用实际上是发送消息 objc_msgSend(receiver, selector, ...)方法决议机制:动态方法解析、…...
JavaSenderMail发送邮件(QQ及OFFICE365)
前言 这是今天处理的公司安排的一个任务:客户系统发送offices365邮件报错535 之前没怎么解除邮件业务,于是先搭个简单的QQ邮件Demo熟悉一下吧,没有啥公网内网的麻烦(之前听说有内网限制,我还处理了一些环境上的问题&…...
八股文--JVM(2)
⭐️⭐️6.类加载 类加载器 JVM只会运行二进制文件,类加载器的作用就是将字节码加载到JVM中,从而让程序启动 1.启动类加载器 ----JAVA_HOME/jre/libC编写加载的是JAVA_HOME/jre/lib 2.拓展类加载器 ----JAVA_HOME/jre/lib/ext 3.应用类加载器 ----C…...
【HTML-3】HTML 中的水平线与换行:基础元素详解
在网页设计中,合理的布局和内容分隔对于提升用户体验至关重要。HTML 提供了两个简单但强大的元素来实现这些功能:水平线 (<hr>) 和换行 (<br>)。本文将深入探讨这两个元素的用法、最佳实践以及现代替代方案。 1. 水平线 <hr> 元素 1…...
绿色云计算:数字化转型与可持续发展的完美融合
目录 引言 绿色云计算的概念与定义 云计算的环境影响与绿色云计算的重要性 绿色云计算的技术实践与策略 绿色云计算的案例研究与最佳实践 绿色云计算的挑战与限制 绿色云计算的未来趋势与预测 结论与展望 引言 随着云计算技术的迅猛发展和广泛应用,其环境影…...
AMO——下层RL与上层模仿相结合的自适应运动优化:让人形行走操作(loco-manipulation)兼顾可行性和动力学约束
前言 自从去年24年Q4,我司侧重具身智能的场景落地与定制开发之后 去年Q4,每个月都会进来新的具身需求今年Q1,则每周都会进来新的具身需求Q2的本月起,一周不止一个需求 特别是本周,几乎每天都有国企、名企通过我司找到…...
大模型——多模态检索的RAG系统架构设计
文章目录 1. 系统架构设计核心组件 2. 跨模态向量空间对齐方案方法一:预训练对齐模型(如CLIP)方法二:跨模态投影网络方法三:联合微调 3. 混合检索策略4. 关键问题解决Q: 如何解决模态间向量尺度不一致?Q: 如…...
BUUCTF——Kookie
BUUCTF——Kookie 进入靶场 一个登录页面 左上角提示让以admin身份登录 找到了cookie 应该与cookie相关 测试了一下admin admin没登上 We found the account cookie / monster 回头看了一下 这个是不是账号密码 测试一下 成功登入 但是没有flag 应该还是跟cookie相关 …...
代码随想录算法训练营
力扣684.冗余连接【medium】 力扣.冗余连接Ⅱ【hard】 一、力扣684.冗余连接【medium】 题目链接:力扣684.冗余连接 left x300 视频链接:代码随想录 题解链接:灵茶山艾府 1、思路 可以从前向后遍历每一条边(因为优先让前面的边连上…...
服务器磁盘不同格式挂载区别
在Linux系统中,磁盘不同格式挂载的核心区别主要体现在文件系统类型和挂载方式两个方面,以下为具体差异分析: 一、文件系统类型区别 磁盘格式即文件系统类型的选择直接影响挂载后的性能和功能: 常见文件系统比较 e…...
AI智能分析网关V4人员摔倒检测打造医院/工厂等多场景智能安全防护体系
一、方案背景 随着全球老龄化加剧,我国老年人口占比持续攀升,老年人摔倒伤亡事件频发,居家、养老机构等场景的摔倒防控成为社会焦点。同时,工厂、医院、学校等人员密集场所也易发生意外摔倒安全事故。传统人工监控存在视觉疲劳…...
window 显示驱动开发-准备 DMA 缓冲区
显示微型端口驱动程序必须及时准备 DMA 缓冲区。 当 GPU 处理 DMA 缓冲区时,通常调用显示微型端口驱动程序来准备下一个 DMA 缓冲区,以便提交到 GPU。 若要防止 GPU 耗尽,显示微型端口驱动程序在准备和提交后续 DMA 缓冲区时所花费的时间必须…...
程序设计实践--排序(1)
1、插入排序(一个数组) #include<bits/stdc.h> using namespace std; const int N1e35; int a[N]; int n; int main(){cin>>n;for(int i1;i<n;i){cin>>a[i];}for(int i1;i<n;i){int va[i];int ji-1;while(j>1&am…...
window 显示驱动开发-GDI 硬件加速
Windows 7 引入的 GDI 硬件加速功能在图形处理单元 (GPU) 上提供加速的核心图形设备接口 (GDI) 操作。 若要指示 GPU 和驱动程序支持此功能,显示微型端口驱动程序必须将DXGKDDI_INTERFACE_VERSION设置为 > DXGKDDI_INTERFACE_VERSION_WIN7。 显示微型端口驱动程…...
驱动开发硬核特训 · Day 31:理解 I2C 子系统的驱动模型与实例剖析
📚 训练目标: 从驱动模型出发,掌握 I2C 子系统的核心结构;分析控制器与从设备的注册流程;结合 AT24 EEPROM 驱动源码与设备树实例,理解 i2c_client 与 i2c_driver 的交互;配套高质量练习题巩固理…...
网络安全之网络攻击spring临时文件利用
0x00 传统攻击流程 我们之前传统的攻击流程由以下几个步骤来完成 攻击者找到可以控制目标JDBC连接fakeServer的地方目标向fakeServer发起连接请求fakeServer向目标下发恶意数据包目标解析恶意数据包并完成指定攻击行为(文件读取、反序列化),…...
统一端点管理(UEM):定义、优势与重要性
统一终端管理(UEM)是一种通过单一平台集中管理、监控和保护企业所有终端设备(如笔记本电脑、移动设备、服务器、物联网设备等)的综合性策略。其核心在于跨操作系统(Windows、macOS、iOS、Android等)实现…...
什么是Rootfs
Rootfs (Root Filesystem) 详解 buildroot工具构建了一个名为"rootfs.tar"的根文件系统压缩包。 什么是rootfs Rootfs(Root Filesystem,根文件系统)是操作系统启动后挂载的第一个文件系统,它包含系统正常运行所需的基…...
黑马Java基础笔记-13常用查找算法
查找算法 基本查找(也叫顺序查找,线性查找) 二分查找(需要有序数据) public static int binarySearch(int[] arr, int number){//1.定义两个变量记录要查找的范围int min 0;int max arr.length - 1;//2.利用循环不断的去找要查找的数据wh…...
#渗透测试#批量漏洞挖掘#LiveBos UploadFile(CVE-2021-77663-2336) 任意文件上传漏洞
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
Git 和 GitHub 学习指南本地 Git 配置、基础命令、GitHub 上传流程、企业开发中 Git 的使用流程、以及如何将代码部署到生产服务器
Windows 上 Git 安装与配置 下载安装:访问 Git 官方网站下载适用于 Windows 的安装程序。运行安装包时会出现许可协议、安装目录、组件选择等界面(如下图)。在“Select Components”页面建议勾选 Git Bash Here 等选项,以便在资源…...
SUI批量转账几种方法介绍
一、Sui区块链简介 Sui是由前Meta(Facebook)工程师创建的下一代Layer 1区块链,采用基于Move编程语言的新型智能合约平台。Sui的设计专注于高吞吐量、低延迟和可扩展性,使其特别适合需要处理大量交易的场景。 Sui的核心特点&…...
Vue2到Vue3迁移问题解析
1. 响应式系统的变化 问题:Vue3 使用 Proxy 替代 Object.defineProperty,导致部分 Vue2 的响应式写法失效。解析: 数组直接索引修改:// Vue2:需使用 Vue.set 或 splice this.$set(this.items, 0, new value); this.it…...
【解决】rpm 包安装成功,但目录不存在问题
开发平台:RedHat 8 一、问题描述 [rootproxy ~]# rpmbuild -ba /root/rpmbuild/SPECS/nginx.spec # rpmbuild 制作 .rpm 包 [rootproxy ~]# yum -y install /root/rpmbuild/RPMS/x86_64/nginx-1.22.1-1.x86_64.rpm # 安装 .rpm包 …...
深度学习框架显存泄漏诊断手册(基于PyTorch的Memory Snapshot对比分析方法)
点击 “AladdinEdu,同学们用得起的【H卡】算力平台”,H卡级别算力,按量计费,灵活弹性,顶级配置,学生专属优惠。 一、显存泄漏:深度学习开发者的"隐形杀手" 在深度学习模型的训练与推…...
PyTorch中单卡训练、DataParallel(DP)和DistributedDataParallel(DDP)
PyTorch中提供了单卡训练、DataParallel(DP)和DistributedDataParallel(DDP),下面是相关原理与实现代码。 代码下载链接:git代码链接 一、单卡训练 原理 单卡训练是最基础的模型训练方式,使用…...
Redis从入门到实战 - 高级篇(中)
一、多级缓存 1. 传统缓存的问题 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,存在下面的问题: 请求要经过Tomcat处理,Tomcat的性能成为整个系统的瓶颈Redis缓存失效时,会…...
项目计划缺乏可行性,如何制定实际可行的计划?
制定实际可行的项目计划需从明确项目目标、准确评估资源、风险管理、设定合理里程碑以及优化沟通渠道入手。其中,明确项目目标尤为关键,只有在目标清晰、具体且量化时,团队才能有效规划各项活动并衡量进展。例如,目标若模糊或过于…...
React中使用ahooks处理业务场景
// 从 ahooks 引入 useDynamicList 钩子函数,用于管理动态列表数据(增删改) import { useDynamicList } from ahooks;// 从 ant-design/icons 引入两个图标组件:减号圆圈图标和加号圆圈图标 import { MinusCircleOutlined, PlusCi…...
CNBC专访CertiK联创顾荣辉:从形式化验证到AI赋能,持续拓展Web3.0信任边界
近日,CertiK联合创始人、哥伦比亚大学教授顾荣辉接受全球知名财经媒体CNBC阿拉伯频道专访,围绕形式化验证的行业应用、AI在区块链安全中的角色,以及新兴技术风险等议题,分享了其对Web3.0安全未来的深刻洞察。 顾荣辉表示…...
基于Spring Boot与jQuery的用户管理系统开发实践✨
引言📚 用户管理系统是企业级应用的核心模块,需实现数据分页、状态管理及高效前后端交互。本文以Spring Boot为后端框架、jQuery为前端工具,构建一个结构清晰的用户管理系统,详解三层架构设计、接口规范及全栈开发流程࿰…...
StreamSaver实现大文件下载解决方案
StreamSaver实现大文件下载解决方案 web端 安装 StreamSaver.js npm install streamsaver # 或 yarn add streamsaver在 Vue 组件中导入 import streamSaver from "streamsaver"; // 确保导入名称正确完整代码修正 <!--* projectName: * desc: * author: dua…...
vue3+echarts 做温度计
参考Echarts 做的温度计_echart温度计-CSDN博客 但是现在这个写法不支持了,更新一下,然后修改了温度值和刻度及单位颜色为黑,初始化echarts写法, itemStyle: {normal: {color: #4577BA,barBorderRadius: 50,}},<div id"main14"…...
鸿蒙开发——7.ArkUI进阶:@BuilderParam装饰器的核心用法与实战解析
鸿蒙开发——7.ArkUI进阶:BuilderParam装饰器的核心用法与实战解析 ArkUI进阶:BuilderParam装饰器的核心用法与实战解析引言一、核心概念速览1.1 什么是BuilderParam?1.2 与Builder的关系 二、核心使用场景2.1 参数初始化组件2.2 尾随闭包初始…...
【数据结构】队列的完整实现
队列的完整实现 队列的完整实现github地址前言1. 队列的概念及其结构1.1 概念1.2 组织结构 2. 队列的实现接口一览结构定义与架构初始化和销毁入队和出队取队头队尾数据获取size和判空 完整代码与功能测试结语 队列的完整实现 github地址 有梦想的电信狗 前言 队列&…...
销售易史彦泽:从效率工具到增长引擎,AI加速CRM不断进化
导读:AI的加入,让CRM实现从“人适配系统”到“系统适配人”,从“管控工具”向“智能助手”跃迁,重构客户关系管理的底层逻辑。 作者 | 小葳 图片来源 | 摄图 AI应用与SaaS的关系,是当前科技与商业领域热议的话题。 当…...
开疆智能Profinet转ModbusTCP网关连接BORUNTE伯朗特系统配置案例
本案例是通过开疆智能Profinet转ModbusTCP网关将西门子PLC与BORUNTE机器人连接的配置案例。具体配置方法如下。 配置过程 Profinet设置 设置网关在Profinet一侧的参数包括(设备名称,IP地址等) 先导入GSD文件 选择GSD所在文件夹位置&#…...
从0到1搭建shopee测评自养号系统:独立IP+硬件伪装+养号周期管理
在跨境电商竞争白热化的背景下,Shopee卖家通过自养号测评实现流量与销量突破已成为行业共识。自养号测评通过模拟真实买家行为,为店铺注入精准流量,同时规避外包测评的高风险与不可控性。本文将从技术架构、运营策略、风险控制三个维度&#…...
arrow-0.1.0.jar 使用教程 - Java jar包运行方法 命令行启动步骤 常见问题解决
准备工作 首先确保你电脑上装了Java环境(JDK 8或以上版本) 把这个jar文件下载到你的电脑上,arrow-0.1.0.jar下载链接:https://pan.quark.cn/s/66d7c061c95a 运行方法 打开命令行(Windows按WinR输入cmd,M…...
请问交换机和路由器的区别?vlan 和 VPN 是什么?
交换机和路由器的区别 特性交换机(Switch)路由器(Router)工作层级数据链路层(L2,基于MAC地址)网络层(L3,基于IP地址)主要功能在局域网(LAN&#…...
如何查看与设置电脑静态IP地址:完整指南
在当今数字化时代,稳定的网络连接已成为工作生活的必需品。静态IP地址作为网络配置中的重要一环,相比动态IP具有更高的稳定性和可控性,然而,许多用户对如何查看和设置静态IP地址仍感到困惑。本文将为您提供从基础概念到实操步骤的…...
Linux网络基础全面解析:从协议分层到局域网通信原理
Linux系列 文章目录 Linux系列前言一、计算机网络背景1.1 认识网络1.2 认识协议 二、网络协议初识2.1 协议分层2.2 OSI七层模型2.3 TCP/IP协议栈2.4 网络协议栈与OS的关系2.5 网络协议在网络传输时的作用 三、网络通信局域网通信的安全隐患与应对总结 前言 Linux系统部分的学习…...
第二篇:服务与需求——让用户找到并预订服务
目录 1 服务类目与项目管理:飞书多维表格为管理中心,微搭小程序展示1.1 需求分析1.2 数据模型:微搭中的服务分类与服务项目(用于小程序展示)1.3 数据模型:多维表格中的服务分类与服务项目 总结 我们已经用了…...
【AI News | 20250520】每日AI进展
AI Repos 1、nanoDeepResearch nanoDeepResearch 是一个受 ByteDance 的 DeerFlow 项目启发,旨在从零开始构建深度研究代理的后端项目。它不依赖 LangGraph 等现有框架,通过实现一个 ReAct 代理和状态机来模拟 Deep Research 的工作流程。项目主要包含规…...
Spark Core基础与源码剖析全景手册
Spark Core基础与源码剖析全景手册 Spark作为大数据领域的明星计算引擎,其核心原理、源码实现与调优方法一直是面试和实战中的高频考点。本文将系统梳理Spark Core与Hadoop生态的关系、经典案例、聚合与分区优化、算子底层原理、集群架构和源码剖析,结合…...