1. 主页 > 好文章

Java多线程编程:4种并发制方法详解与代码实现


当你的程序像春运抢票一样混乱,该怎么办?

你有没有遇到过这种情况?明明写了个计数器程序,10个线程各加1000次,结果总不是10000?又或者APP突然卡死,日志里全是"死锁"报错?这都跟??多线程翻车现场??有关。今天咱们就来盘一盘Java里最常用的4把"锁头",保准你看完就能把混乱的线程治得服服帖帖!


一、synchronized:新手村的防身匕首

??1. 基础用法:给代码穿防弹衣??

这玩意儿用起来简单到哭,就像给方法戴个安全帽:

java复制
public class Counter {
    private int count = 0;
    
    // 直接给方法加锁
    public synchronized void add() {
        count++;
    }
}

或者给代码块加个结界:

java复制
public void add() {
    synchronized(this) {  // 锁住当前对象
        count++;
    }
}

??适用场景??:简单粗暴搞定计数器、订单状态变更这些基础操作。

??2. 隐藏陷阱:你以为的锁可能是个假锁??

  • ??锁对象不对??:比如用Integer当锁,结果自动装箱变成不同对象
  • ??锁粒度太大??:把整个方法锁死,性能直接打骨折
  • ??不可重入???不存在的!Java的synchronized自带重入技能

??真实翻车案例??:
某电商用synchronized锁库存对象,结果促销时数据库连接池爆了——因为所有线程都在等同一把锁,最后改成分布式锁才救场。


二、ReentrantLock:老司机的瑞士军刀

??1. 灵活在哪?三大绝活秒杀synchronized??

java复制
Lock lock = new ReentrantLock();
public void transfer(Account from, Account to) {
    lock.lock();
    try {
        // 转账操作
    } finally {
        lock.unlock(); // 必须手动释放!
    }
}

??核心优势??:

  • ??可中断??:等锁等烦了可以喊停(lockInterruptibly())
  • ??超时机制??:设定最多等5秒(tryLock(5, TimeUnit.SECONDS))
  • ??公平锁??:杜绝插队狗,按先来后到执行

??性能对比??:
在百万次操作测试中,ReentrantLock比synchronized快20%左右,但前提是你会用!

??2. 使用禁忌:别把自己玩死了??

  • 忘记unlock?恭喜喜提内存泄漏大礼包
  • 在循环里乱用tryLock?CPU分分钟飙到100%
  • 以为用了公平锁就高枕无忧?吞吐量可能掉一半

三、原子类:无锁化的黑科技

??1. CAS原理:线程界的以物易物??

java复制
AtomicInteger atomicCount = new AtomicInteger(0);
public void add() {
    atomicCount.incrementAndGet(); // 原子操作
}

底层用的是CPU的CAS指令(Compare And Swap),就像去小卖部换东西:"老板,我用这瓶可乐换你两包辣条,要没人动过货架才行啊!"

??2. 性能炸裂:比锁快10倍的秘密??

在8核CPU环境下测试:

方式100万次操作耗时
synchronized235ms
ReentrantLock198ms
AtomicInteger23ms

但注意ABA问题——就像你女朋友出门前把口红从A色号换成B又换回A,你以为没变过?这时候就需要用AtomicStampedReference加版本号。


四、线程池:管理大师的终极奥义

??1. 为什么要用?举个血淋淋的例子??

某APP上线初期直接new Thread,结果:

  • 每天创建10万+线程,内存爆了3次
  • 上下文切换吃掉70%CPU
  • 最后用线程池改造,服务器从8台减到2台

??2. 四种经典池子怎么选??

java复制
// 单线程池(保证顺序执行)
ExecutorService single = Executors.newSingleThreadExecutor();

// 固定线程池(处理突发流量)
ExecutorService fixed = Executors.newFixedThreadPool(10);

// 缓存线程池(适合短时任务)
ExecutorService cached = Executors.newCachedThreadPool();

// 定时任务池(闹钟功能)
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);

??选型口诀??:

  • 定时任务用scheduled
  • 长期驻守用fixed
  • 来去匆匆用cached
  • 特殊需求上ThreadPoolExecutor自定义

个人观点:别把锁当万能药

在Java多线程江湖混了这么多年,有三条血泪经验:

  1. ??能不用锁就别用??:90%的并发问题其实用CopyOnWriteArrayList这类并发集合就能解决
  2. ??锁的代价比想象的大??:曾经有个系统加了锁反而慢了10倍,后来发现是锁竞争太激烈
  3. ??监控比代码重要??:用Arthas的monitor命令随时看锁等待情况,比埋日志管用100倍

下次遇到线程问题时,先灵魂三问:

  • 这数据真的需要共享吗?
  • 能不能用副本代替直接操作?
  • 有没有更轻量的解决方案?

记住,最高明的并发控制是让线程们根本不需要打架!

本文由嘻道妙招独家原创,未经允许,严禁转载