写业务代码时你可能写过这样的代码// 多线程并发修改共享数据privateintcount0;publicvoidincrement(){count;// 非原子操作有并发问题}这就是典型的线程安全问题。多个线程同时修改同一个数据导致结果不可预期。理解线程和 synchronized能帮你写出线程安全的代码理解 Java 并发问题的根因为学习更高级的并发工具打基础下面我按「线程状态 → 线程创建 → synchronized 基础 → 锁升级」的顺序往下聊。1. 线程的六种状态 1.1 线程状态概览Java 中线程有六种状态// Thread.State 枚举publicenumState{NEW,// 新建RUNNABLE,// 可运行BLOCKED,// 阻塞WAITING,// 等待TIMED_WAITING,// 超时等待TERMINATED// 终止}1.2 状态转换图线程状态转换 │ NEW → START │ RUNNABLE ←→ BLOCKEDsynchronized 锁竞争 │ RUNNABLE ←→ WAITINGObject.wait()、Thread.join()、LockSupport.park() │ RUNNABLE ←→ TIMED_WAITINGThread.sleep()、Object.wait(long)、LockSupport.parkNanos() │ RUNNABLE → TERMINATED1.3 各状态详解NEW新建// 创建线程但未启动ThreadthreadnewThread(()-System.out.println(Hello));// 状态NEWRUNNABLE可运行// 启动线程thread.start();// 状态RUNNABLE可能正在运行也可能等待 CPU 时间片BLOCKED阻塞// 等待获取 synchronized 锁synchronized(lock){// 线程进入 BLOCKED 状态等待获取锁}WAITING无限期等待// Object.wait()synchronized(lock){lock.wait();// 释放锁进入 WAITING}// Thread.join()thread.join();// 等待 thread 结束// LockSupport.park()LockSupport.park();// 阻塞当前线程TIMED_WAITING超时等待// Thread.sleep()Thread.sleep(1000);// 1 秒// Object.wait(long)synchronized(lock){lock.wait(1000);// 最多等 1 秒// Thread.join(long)thread.join(1000);// LockSupport.parkNanos()LockSupport.parkNanos(1000000);// 1 毫秒TERMINATED终止// 线程执行完毕thread.join();// 等待 thread 结束// 状态TERMINATED2. 线程的创建与启动 2.1 继承 ThreadclassMyThreadextendsThread{Overridepublicvoidrun(){System.out.println(Thread running);}}// 启动MyThreadtnewMyThread();t.start();2.2 实现 RunnableclassMyRunnableimplementsRunnable{Overridepublicvoidrun(){System.out.println(Runnable running);}}// 启动ThreadtnewThread(newMyRunnable());t.start();// Lambda 简化ThreadtnewThread(()-System.out.println(Lambda running));t.start();2.3 实现 CallableclassMyCallableimplementsCallableInteger{OverridepublicIntegercall()throwsException{return42;}}// 启动需要 FutureTaskFutureTaskIntegertasknewFutureTask(newMyCallable());newThread(task).start();Integerresulttask.get();// 获取返回值2.4 线程池// 创建线程池ExecutorServiceexecutorExecutors.newFixedThreadPool(4);// 提交任务executor.submit(()-System.out.println(Task running));// 关闭executor.shutdown();3. synchronized 基础 3.1 什么是 synchronizedsynchronized是 Java 的内置锁保证同一时刻只有一个线程执行同步代码块。// 修饰实例方法publicsynchronizedvoidincrement(){count;}// 修饰代码块publicvoidincrement(){synchronized(this){count;}}// 修饰静态方法publicstaticsynchronizedvoidstaticIncrement(){staticCount;}3.2 synchronized 的特性特性说明原子性保证代码块原子执行可见性保证内存可见性阻塞性未获取锁的线程阻塞可重入同一个线程可以多次获取3.3 可重入性publicsynchronizedvoidmethodA(){System.out.println(A);methodB();// 可以重入}publicsynchronizedvoidmethodB(){System.out.println(B);}// 同一个线程可以多次获取锁3.4 synchronized 的使用场景// 场景 1计数器privateintcount0;publicsynchronizedvoidincrement(){count;}publicsynchronizedintgetCount(){returncount;}// 场景 2单例模式publicclassSingleton{privatestaticSingletoninstance;publicstaticsynchronizedSingletongetInstance(){if(instancenull){instancenewSingleton();}returninstance;}}// 场景 3生产者-消费者publicclassBuffer{privateQueueStringqueuenewLinkedList();privateintcapacity10;publicsynchronizedvoidput(Stringitem)throwsInterruptedException{while(queue.size()capacity){wait();// 等待消费者消费}queue.add(item);notifyAll();// 通知消费者}publicsynchronizedStringtake()throwsInterruptedException{while(queue.isEmpty()){wait();// 等待生产者生产}Stringitemqueue.poll();notifyAll();// 通知生产者returnitem;}}4. synchronized 底层原理 4.1 Monitor监视器锁synchronized底层依赖Monitor监视器锁实现Monitor 结构 │ ├─ Owner拥有锁的线程 ├─ WaitSet等待队列调用 wait() 的线程 ├─ EntryList阻塞队列等待获取锁的线程 └─ Count计数器重入次数4.2 锁的升级过程synchronized锁会根据竞争情况自动升级锁升级过程 │ ├─ 无锁 │ ├─ 偏向锁第一次获取锁 │ └─ 记录线程 ID下次进入无需同步 │ ├─ 轻量级锁多个线程竞争 │ └─ 自旋 CAS 获取锁 │ └─ 重量级锁自旋失败 └─ 阻塞等待4.3 偏向锁第一次获取锁时使用偏向锁// 偏向锁记录线程 ID// 下次同一线程进入同步块无需任何同步操作synchronized(this){// ...}优点无同步开销缺点有竞争时需要撤销4.4 轻量级锁多个线程交替获取锁时使用轻量级锁// 轻量级锁CAS 自旋// 线程在栈帧中创建锁记录Lock Record// 通过 CAS 将锁记录地址复制到对象头的 Mark Wordsynchronized(this){// ...}优点避免线程阻塞缺点自旋消耗 CPU4.5 重量级锁多线程竞争激烈时升级为重量级锁// 重量级锁Monitor 机制// 未获取锁的线程阻塞等待唤醒synchronized(this){// ...}优点竞争激烈时效率高缺点线程阻塞切换开销大5. 常见问题与注意事项 ⚠️5.1 锁对象不能为 null// ❌ 错误Objectlocknull;synchronized(lock){// NullPointerException}// ✅ 正确ObjectlocknewObject();synchronized(lock){}5.2 锁的范围要适当// ❌ 锁范围过大publicsynchronizedvoidprocess(){// 大量无需同步的代码downloadFile();// 耗时操作parseData();saveToDb();}// ✅ 锁范围适当publicvoidprocess(){downloadFile();parseData();synchronized(this){saveToDb();// 只锁关键部分}}5.3 避免死锁// ❌ 可能死锁publicvoidmethod1(){synchronized(lockA){synchronized(lockB){// ...}}}publicvoidmethod2(){synchronized(lockB){// 顺序相反synchronized(lockA){// ...}}}// ✅ 统一锁顺序publicvoidmethod1(){synchronized(lockA){synchronized(lockB){// ...}}}publicvoidmethod2(){synchronized(lockA){// 顺序一致synchronized(lockB){// ...}}}5.4 静态 synchronized vs 实例 synchronizedclassUser{// 锁的是 User.class 对象publicstaticsynchronizedvoidstaticMethod(){// 同一时刻只有一个线程执行}// 锁的是 this实例对象publicsynchronizedvoidinstanceMethod(){// 同一时刻只有一个线程执行同一实例}}6. 常见面试题 6.1 synchronized 和 Lock 的区别特性synchronizedLock语法关键字接口获取锁自动释放手动获取/释放阻塞是可选择公平锁否可选择条件变量无有6.2 synchronized 修饰 static 方法和普通方法的区别static 方法锁的是 Class 对象普通方法锁的是 this实例对象6.3 什么是可重入锁同一个线程可以多次获取同一把锁不会被自己阻塞。6.4 锁升级的过程无锁 → 偏向锁 → 轻量级锁 → 重量级锁6.5 如何排查死锁# jstack 排查死锁jstackpid# 输出包含 Found 1 deadlock.小结线程六种状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED线程创建继承 Thread、实现 Runnable、实现 Callable、线程池synchronized保证原子性、可见性、可重入锁升级偏向锁 → 轻量级锁 → 重量级锁注意事项锁对象不能为 null、锁范围要适当、避免死锁下一篇027预告volatile、happens-before 入门——可见性、指令重排、内存屏障、JMM 模型。