Java并发编程实战第五章:构建并发模块的技术要点解析

Java并发编程实战第五章:构建并发模块的技术要点解析

【免费下载链接】booknotes A collection of my book notes on various computer science books 【免费下载链接】booknotes 项目地址: https://gitcode.com/gh_mirrors/bo/booknotes

引言:并发编程的基础构建块

在Java并发编程的世界中,第五章"Building Blocks"为我们揭示了构建健壮、高效并发应用的核心技术组件。你是否曾经遇到过这样的困境:在多线程环境下使用集合时遭遇ConcurrentModificationException,或者因为不当的同步策略导致性能瓶颈?本章正是为了解决这些痛点而生,系统地介绍了Java并发包中那些经过实战检验的构建模块。

通过阅读本文,你将掌握:

  • 同步集合与并发集合的深度对比与选择策略
  • 阻塞队列在生产者-消费者模式中的精妙应用
  • 四大同步器(Latches、FutureTask、Semaphores、Barriers)的原理与实践
  • 构建高效可伸缩结果缓存的最佳实践方案

同步集合:传统方案的局限性

同步包装器的内在缺陷

Java早期的线程安全集合通过Collections.synchronizedXxx()方法实现,这种方案采用Java监视器模式(Java Monitor Pattern),在每个公共方法上添加同步锁。

// 问题代码示例:看似安全实则存在竞态条件
public static Object getLast(Vector list) {
    int lastIndex = list.size() - 1;    // 可能被其他线程修改
    return list.get(lastIndex);         // 可能越界
}

public static void deleteLast(Vector list) {
    int lastIndex = list.size() - 1;    // 可能被其他线程修改
    list.remove(lastIndex);             // 可能越界
}

复合操作的安全隐患

同步集合最大的问题在于不支持原子性的复合操作。即使是简单的"检查后执行"模式也需要客户端显式加锁:

// 正确的客户端加锁方式
public static Object getLast(Vector list) {
    synchronized (list) {
        int lastIndex = list.size() - 1;
        return list.get(lastIndex);
    }
}

迭代器的并发修改异常

迭代操作在多线程环境下尤为脆弱,即使使用现代并发集合,修改期间的迭代仍可能抛出ConcurrentModificationException。解决方案包括:

  1. 完全锁定集合:保证迭代期间的一致性,但牺牲并发性
  2. 克隆集合:迭代副本,但克隆操作仍需同步

隐藏迭代器的陷阱

某些看似无害的操作实际上隐含迭代行为:

public class HiddenIterator {
    private final Set<Integer> set = new HashSet<>();
    
    public void addTenThings() {
        for (int i = 0; i < 10; i++)
            set.add(i);
        System.out.println("DEBUG: added elements to " + set); // 隐含toString()迭代
    }
}

并发集合:现代Java的解决方案

ConcurrentHashMap的革命性设计

ConcurrentHashMap采用分段锁技术,大幅提升并发性能:

mermaid

原子性Map操作

ConcurrentHashMap通过接口方法提供原子复合操作:

public interface ConcurrentMap<K,V> {
    V putIfAbsent(K key, V value);          // 不存在时插入
    boolean remove(K key, V value);         // 精确匹配删除
    boolean replace(K key, V oldValue, V newValue); // 条件替换
    V replace(K key, V newValue);           // 无条件替换
}

CopyOnWriteArrayList的写时复制策略

适用于读多写少的场景,迭代期间保证一致性:

List<String> list = new CopyOnWriteArrayList<>();
// 写操作:复制整个数组,修改副本,替换引用
// 读操作:直接访问当前数组引用,无需同步

阻塞队列与生产者-消费者模式

阻塞队列的核心机制

阻塞队列通过put()take()方法实现线程间协调:

mermaid

桌面搜索实例分析

public class FileCrawler implements Runnable {
    private final BlockingQueue<File> fileQueue;
    
    private void crawl(File root) throws InterruptedException {
        File[] entries = root.listFiles();
        for (File entry : entries) {
            if (entry.isDirectory()) {
                crawl(entry);
            } else if (!alreadyIndexed(entry)) {
                fileQueue.put(entry); // 阻塞直到有空间
            }
        }
    }
}

public class Indexer implements Runnable {
    public void run() {
        try {
            while (true) {
                File file = queue.take(); // 阻塞直到有文件
                indexFile(file);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

串行线程限制模式

阻塞队列实现了对象所有权的安全转移:

  1. 生产者:创建对象并放入队列后不再使用
  2. 队列:作为中间媒介,保证安全发布
  3. 消费者:从队列取出后获得独占访问权

工作窃取算法

Java 6引入的Deque支持工作窃取模式:

mermaid

同步器:高级线程协调机制

CountDownLatch:多线程启动协调

public class TestHarness {
    public long timeTasks(int nThreads, Runnable task) throws InterruptedException {
        CountDownLatch startGate = new CountDownLatch(1);    // 开始门
        CountDownLatch endGate = new CountDownLatch(nThreads); // 结束门
        
        for (int i = 0; i < nThreads; i++) {
            new Thread(() -> {
                try {
                    startGate.await();      // 等待开始信号
                    task.run();
                } finally {
                    endGate.countDown();    // 通知任务完成
                }
            }).start();
        }
        
        long start = System.nanoTime();
        startGate.countDown();              // 释放所有线程
        endGate.await();                    // 等待所有线程完成
        return System.nanoTime() - start;
    }
}

FutureTask:异步结果获取

public class Preloader {
    private final FutureTask<ProductInfo> future = 
        new FutureTask<>(() -> loadProductInfo());
    
    public ProductInfo get() throws Exception {
        try {
            return future.get(); // 阻塞直到结果就绪
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();
            if (cause instanceof DataLoadException) {
                throw (DataLoadException) cause;
            } else {
                throw new RuntimeException(cause);
            }
        }
    }
}

Semaphore:资源访问控制

信号量实现有界集合:

public class BoundedHashSet<T> {
    private final Set<T> set;
    private final Semaphore semaphore;
    
    public boolean add(T element) throws InterruptedException {
        semaphore.acquire();            // 获取许可
        boolean added = false;
        try {
            added = set.add(element);
            return added;
        } finally {
            if (!added) {
                semaphore.release();    // 添加失败时释放许可
            }
        }
    }
    
    public boolean remove(Object element) {
        boolean removed = set.remove(element);
        if (removed) {
            semaphore.release();        // 移除成功时释放许可
        }
        return removed;
    }
}

CyclicBarrier:多阶段同步

适用于分阶段并行算法:

public class CellularAutomata {
    private final CyclicBarrier barrier;
    private final Worker[] workers;
    
    public CellularAutomata() {
        int count = Runtime.getRuntime().availableProcessors();
        barrier = new CyclicBarrier(count, () -> {
            // 所有worker完成后的回调
            mainBoard.commitNewValues();
        });
        
        workers = new Worker[count];
        for (int i = 0; i < count; i++) {
            workers[i] = new Worker(mainBoard.getSubBoard(count, i));
        }
    }
    
    private class Worker implements Runnable {
        public void run() {
            while (!board.hasConverged()) {
                // 计算自己负责的区域
                for (int x = 0; x < board.getMaxX(); x++) {
                    for (int y = 0; y < board.getMaxY(); y++) {
                        board.setNewValue(x, y, computeValue(x, y));
                    }
                }
                
                try {
                    barrier.await(); // 等待其他worker
                } catch (Exception e) {
                    return;
                }
            }
        }
    }
}

构建高效可伸缩结果缓存

演进历程:从简单到完善

版本1:同步方法实现
public class Memoizer1<A, V> implements Computable<A, V> {
    private final Map<A, V> cache = new HashMap<>();
    
    public synchronized V compute(A arg) throws InterruptedException {
        V result = cache.get(arg);
        if (result == null) {
            result = c.compute(arg);
            cache.put(arg, result);
        }
        return result;
    }
}

问题:完全串行化,性能极差

版本2:ConcurrentHashMap实现
public class Memoizer2<A, V> implements Computable<A, V> {
    private final Map<A, V> cache = new ConcurrentHashMap<>();
    
    public V compute(A arg) throws InterruptedException {
        V result = cache.get(arg);
        if (result == null) {
            result = c.compute(arg);    // 可能重复计算
            cache.put(arg, result);
        }
        return result;
    }
}

问题:存在竞态条件,可能重复计算

版本3:FutureTask包装
public class Memoizer3<A, V> implements Computable<A, V> {
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
    
    public V compute(A arg) throws InterruptedException {
        Future<V> future = cache.get(arg);
        if (future == null) {
            FutureTask<V> ft = new FutureTask<>(() -> c.compute(arg));
            future = ft;
            cache.put(arg, ft);
            ft.run();
        }
        return future.get();
    }
}

问题:仍存在检查后执行竞态条件

最终版本:原子操作完善
public class Memoizer<A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<>();
    
    public V compute(A arg) throws InterruptedException {
        while (true) {
            Future<V> future = cache.get(arg);
            if (future == null) {
                FutureTask<V> ft = new FutureTask<>(() -> c.compute(arg));
                future = cache.putIfAbsent(arg, ft); // 原子操作
                if (future == null) {
                    future = ft;
                    ft.run();
                }
            }
            
            try {
                return future.get();
            } catch (CancellationException e) {
                cache.remove(arg, future); // 移除已取消的任务
            } catch (ExecutionException e) {
                throw launderThrowable(e.getCause());
            }
        }
    }
}

性能对比与选择指南

并发集合选择矩阵

场景特征推荐方案优点缺点
读多写少CopyOnWriteArrayList迭代安全,无锁读写操作成本高
高并发读写ConcurrentHashMap分段锁,高吞吐弱一致性迭代
顺序处理BlockingQueue生产者-消费者解耦需要边界管理
资源池Semaphore精确控制并发数需要手动释放

同步器适用场景总结

同步器类型核心用途典型场景
CountDownLatch一次性事件等待服务启动协调,批量任务完成等待
FutureTask异步结果获取预加载,并行计算
Semaphore资源访问控制连接池,限流器
CyclicBarrier多阶段同步并行算法,多阶段计算

最佳实践与陷阱避免

中断处理规范

public class TaskRunnable implements Runnable {
    public void run() {
        try {
            processTask(queue.take());
        } catch (InterruptedException e) {
            // 恢复中断状态,不要吞没异常
            Thread.currentThread().interrupt();
        }
    }
}

内存一致性保证

所有并发集合都提供happens-before保证:

  • 将对象放入并发集合的操作happens-before后续的访问操作
  • 从并发集合获取对象的操作happens-before后续的使用操作

性能监控与调优

// 监控缓存命中率
public class MonitoredMemoizer<A, V> extends Memoizer<A, V> {
    private final AtomicLong hits = new AtomicLong();
    private final AtomicLong misses = new AtomicLong();
    
    @Override
    public V compute(A arg) throws InterruptedException {
        Future<V> future = cache.get(arg);
        if (future != null) {
            hits.incrementAndGet();
        } else {
            misses.incrementAndGet();
        }
        return super.compute(arg);
    }
    
    public double getHitRatio() {
        long total = hits.get() + misses.get();
        return total == 0 ? 0 : (double) hits.get() / total;
    }
}

结语:构建健壮并发系统的艺术

Java并发编程实战第五章为我们提供了一套完整的并发构建模块工具箱。从基础的同步集合到高级的同步器,每一个组件都有其特定的适用场景和优化策略。关键在于根据具体的业务需求、性能要求和一致性需求做出恰当的选择。

记住并发编程的黄金法则:优先使用现有的线程安全组件,而不是自己实现同步逻辑。Java并发包中的这些构建模块经过了无数项目的检验,能够帮助我们在保证线程安全的同时,获得最佳的性能表现。

通过深入理解这些构建模块的工作原理和适用场景,你将能够设计出既正确又高效的并发系统,从容应对多线程环境下的各种挑战。

【免费下载链接】booknotes A collection of my book notes on various computer science books 【免费下载链接】booknotes 项目地址: https://gitcode.com/gh_mirrors/bo/booknotes

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值