Java线程同步哪几种方式?什么情况下用哪种方式?

1 个回答

  • 追日
    先解决心情在解决事情。
    # 一、核心分类与什么时候用 ## 1) 互斥锁 / 条件等待 * **`synchronized`**(内置锁) * 用在:简单临界区、可重入、无须中断/超时/多条件队列。 * 优点:语法最简单,JIT 优化成熟;异常时自动释放。 * 不足:无法 `tryLock/lockInterruptibly`,无法多个条件队列。 * **`ReentrantLock`**(AQS) * 用在:需要**可中断获取**、**可超时**、**可公平**、**多 `Condition`** 或需要与 `tryLock` 结合做降级/回退。 * 写法:`lock(); try { ... } finally { unlock(); }` * 还能配 `Condition` 做精准的 `await()/signal()`。 * **`wait/notify`**(基于 `synchronized` 的条件等待) * 用在:历史代码/极简场景。新代码优先 `Condition`。 * 要点:必须在持有该对象监视器时调用;配合 `while` 循环防虚假唤醒。 ## 2) 读写与乐观读 * **`ReentrantReadWriteLock`**(AQS) * 用在:**读多写少**、读操作较重、可容忍写时阻塞所有读。 * 要点:避免在持锁期间做 IO/远程调用。 * **`StampedLock`**(乐观读) * 用在:极端**读多写极少**、读路径很短、可接受读校验失败重读。 * 要点:**不可重入**、无条件队列、对中断支持一般,慎用在复杂调用链。 ## 3) 无锁 / 原子变量(CAS) * **`volatile`** * 用在:只需**可见性/有序性**的标志位(如停止标记)、DCL 单例的引用。 * 注意:不保证复合操作原子性(自增、自减等)。 * **`Atomic*` / `AtomicReference` / `VarHandle`** * 用在:轻量原子更新、状态机、锁竞争高但临界区极短。 * 例:`compareAndSet`、原子指针交换。 * **`LongAdder` / `DoubleAdder`** * 用在:高并发计数/累计(如 QPS/计数器),比 `AtomicLong.incrementAndGet()` 冲突更小。 ## 4) 协作型同步器 * **`Semaphore`**(AQS) * 用在:限流/连接池许可、控制并发度(N 把“许可证”)。 * **`CountDownLatch`**(AQS) * 用在:**一次性**等待多个任务结束(主线程等待 N 个子任务完成)。 * **`CyclicBarrier`** * 用在:**阶段性**对齐(N 个线程“到齐”再一起进入下一阶段)。 * **`Phaser`** * 用在:更灵活的多阶段协作(动态注册/注销参与方)。 * **`Exchanger`** * 用在:两线程间成对交换数据(如生产/清洗两阶段批处理)。 ## 5) 队列/并发容器(用“消息”代替锁) * **`BlockingQueue`**(`ArrayBlockingQueue`、`LinkedBlockingQueue`、`SynchronousQueue`、`LinkedTransferQueue`) * 用在:生产者-消费者,解耦 + 背压最稳。 * 选型:小而固定→`ArrayBlockingQueue`;大而弹性→`LinkedBlockingQueue`;零缓存直连→`SynchronousQueue`;有“直传”→`LinkedTransferQueue`。 * **并发集合**:`ConcurrentHashMap`、`ConcurrentLinkedQueue`、`CopyOnWriteArrayList` * 用在:Map 并发读写(`computeIfAbsent/putIfAbsent`)、高读少写的快照读(COW)。 * **`ThreadLocal`**(规避共享) * 用在:每线程独享状态(日期格式器、缓冲区),**不是同步**,是避免共享从而不需要同步。 # 二、怎么选(速查) * **临界区简单 & 需求普通** → `synchronized`。 * **需要可中断/可超时/公平/多条件队列** → `ReentrantLock`(+ `Condition`)。 * **读多写少** → `ReentrantReadWriteLock`;读极多写极少且读很短 → `StampedLock` 乐观读。 * **高并发计数** → `LongAdder`;**单点状态原子更新** → `Atomic*`/`AtomicReference`。 * **限流/资源并发数** → `Semaphore`。 * **主线程等 N 个子任务** → `CountDownLatch`。 * **多线程分阶段齐步走** → `CyclicBarrier` / `Phaser`(阶段多、参与者可变选 `Phaser`)。 * **生产者-消费者/解耦** → `BlockingQueue`(优先用队列代替人工 `wait/notify`)。 * **两线程互换批次数据** → `Exchanger`。 * **能不共享就不共享** → `ThreadLocal`/不可变对象/消息传递。 # 三、精简代码片段 ```java // ReentrantLock + tryLock 超时 ReentrantLock lock = new ReentrantLock(); if (lock.tryLock(50, TimeUnit.MILLISECONDS)) { try { /* 临界区 */ } finally { lock.unlock(); } } else { /* 降级/丢弃/排队 */ } // 多条件队列 class BoundedBuffer<T> { final ReentrantLock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(), notEmpty = lock.newCondition(); final Object[] buf = new Object[100]; int put, take, count; void put(T x) throws InterruptedException { lock.lock(); try { while (count == buf.length) notFull.await(); buf[put] = x; put = (put + 1) % buf.length; count++; notEmpty.signal(); } finally { lock.unlock(); } } T take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); @SuppressWarnings("unchecked") T v = (T) buf[take]; take = (take + 1) % buf.length; count--; notFull.signal(); return v; } finally { lock.unlock(); } } } // ReadWriteLock ReadWriteLock rw = new ReentrantReadWriteLock(); rw.readLock().lock(); try { /* 多读 */ } finally { rw.readLock().unlock(); } rw.writeLock().lock(); try { /* 少写 */ } finally { rw.writeLock().unlock(); } // StampedLock 乐观读 StampedLock sl = new StampedLock(); long stamp = sl.tryOptimisticRead(); int local = value; // 读取共享变量快照 if (!sl.validate(stamp)) { // 有写发生,退化为读锁 stamp = sl.readLock(); try { local = value; } finally { sl.unlockRead(stamp); } } // CountDownLatch CountDownLatch latch = new CountDownLatch(n); for (...) pool.submit(() -> { try { work(); } finally { latch.countDown(); }}); latch.await(); // 等所有任务完成 // Semaphore 限流 Semaphore sem = new Semaphore(100); if (sem.tryAcquire()) { try { handle(); } finally { sem.release(); } } else { reject(); } // LongAdder 计数 LongAdder counter = new LongAdder(); counter.increment(); long n = counter.sum(); ``` # 四、实战注意 * 持锁区**短小**且不做阻塞/IO;必要时早解锁、后处理。 * 锁顺序一致避免死锁;用 `tryLock` + 超时做检测/降级。 * `wait/notify` 一定**在同步块内**,并用 `while` 再检条件。 * 不要把 `volatile` 当原子计数器;用 `Atomic*`/`LongAdder`。 * 读写锁下,读里绝不能升级为写;需要时释放读锁后再获取写锁。 * 并发 Map 用 `computeIfAbsent/putIfAbsent` 替代 DCL 自己玩锁。
    发布于 2025-08-19 07:49 •IP属地 黑龙江
    举报
    评论
    收藏