java并发编程核心是解决共享资源竞争与任务执行效率瓶颈,需根据io密集型场景合理使用多线程,避免在cpu密集型或锁粒度不当等场景滥用,关键在于精准识别临界区并选用合适同步工具。

Java并发编程核心解决的是「共享资源竞争」和「任务执行效率瓶颈」
它不是为多线程而多线程,而是当单线程无法满足性能、响应性或资源利用率要求时,用可控方式协调多个线程访问共享状态。典型痛点包括:数据不一致(如两个线程同时修改count++)、响应卡死(UI线程被耗时IO阻塞)、吞吐量上不去(数据库连接池空闲但业务线程全在等锁)。
什么时候必须用多线程:IO密集型任务不可回避
网络请求、文件读写、数据库查询这类操作大部分时间在等外部系统返回,单线程会空转。用多线程可让CPU在等待期间处理其他任务:
- Web服务器用线程池处理并发HTTP请求,避免一个慢接口拖垮整个服务
- 爬虫程序并发抓取多个URL,总耗时接近最慢那个请求,而非所有请求耗时之和
-
CompletableFuture.supplyAsync()包装阻塞IO调用,配合thenApply做后续计算,不阻塞主线程
注意:Thread.sleep(1000)不算真正IO,只是模拟等待,实际中应替换为HttpClient或JDBC等真实阻塞调用。
什么时候不该滥用多线程:CPU密集型任务要谨慎
纯计算任务(如图像渲染、加密解密)增加线程数超过CPU核心数,反而因上下文切换开销导致性能下降:
立即学习“Java免费学习笔记(深入)”;
- 用
ForkJoinPool.commonPool()跑并行流(list.parallelStream().map(...))时,若每个元素处理耗时极短,线程调度成本可能超过收益 -
Runtime.getRuntime().availableProcessors()是估算并行度的起点,不是硬上限;需结合监控(如jstack看线程阻塞率)调整 - 共享变量频繁读写(如
volatile int counter)仍可能因缓存一致性协议(MESI)引发伪共享,比预想更慢
最容易被忽略的坑:线程安全 ≠ 加了synchronized就万事大吉
很多开发者以为给方法加synchronized就能防并发问题,但实际失效场景很常见:
- 锁对象不一致:
new A().doSomething()和new A().doSomething()用的是不同实例锁,互不干扰 - 复合操作未原子化:即使
get()和set()各自同步,if (map.get(k) == null) map.put(k, v)仍是竞态 - 内部状态逃逸:在
synchronized块内返回可变对象引用(如return this.list),外部可绕过锁直接修改 - 使用
ConcurrentHashMap时误调size()——它不保证实时准确,高并发下可能反复扩容导致结果抖动
真正关键的是识别「临界区」边界,以及确认锁的粒度是否覆盖所有依赖状态变更的操作。没有银弹,只有根据具体数据结构和访问模式选对工具:AtomicInteger适合计数,ReentrantLock支持条件等待,StampedLock读多写少时更轻量。











