news 2026/4/27 19:50:24

【026】线程状态与 synchronized 基础

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【026】线程状态与 synchronized 基础

写业务代码时,你可能写过这样的代码:

// 多线程并发修改共享数据privateintcount=0;publicvoidincrement(){count++;// 非原子操作,有并发问题}

这就是典型的线程安全问题。多个线程同时修改同一个数据,导致结果不可预期。

理解线程和 synchronized,能帮你:

  • 写出线程安全的代码
  • 理解 Java 并发问题的根因
  • 为学习更高级的并发工具打基础

下面我按「线程状态 → 线程创建 → synchronized 基础 → 锁升级」的顺序往下聊。


1. 线程的六种状态 🔄

1.1 线程状态概览

Java 中线程有六种状态

// Thread.State 枚举publicenumState{NEW,// 新建RUNNABLE,// 可运行BLOCKED,// 阻塞WAITING,// 等待TIMED_WAITING,// 超时等待TERMINATED// 终止}

1.2 状态转换图

线程状态转换: │ NEW → START │ RUNNABLE ←→ BLOCKED(synchronized 锁竞争) │ RUNNABLE ←→ WAITING(Object.wait()、Thread.join()、LockSupport.park()) │ RUNNABLE ←→ TIMED_WAITING(Thread.sleep()、Object.wait(long)、LockSupport.parkNanos()) │ RUNNABLE → TERMINATED

1.3 各状态详解

NEW(新建)
// 创建线程,但未启动Threadthread=newThread(()->System.out.println("Hello"));// 状态:NEW
RUNNABLE(可运行)
// 启动线程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 结束// 状态:TERMINATED

2. 线程的创建与启动 🚀

2.1 继承 Thread

classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("Thread running");}}// 启动MyThreadt=newMyThread();t.start();

2.2 实现 Runnable

classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("Runnable running");}}// 启动Threadt=newThread(newMyRunnable());t.start();// Lambda 简化Threadt=newThread(()->System.out.println("Lambda running"));t.start();

2.3 实现 Callable

classMyCallableimplementsCallable<Integer>{@OverridepublicIntegercall()throwsException{return42;}}// 启动(需要 FutureTask)FutureTask<Integer>task=newFutureTask<>(newMyCallable());newThread(task).start();Integerresult=task.get();// 获取返回值

2.4 线程池

// 创建线程池ExecutorServiceexecutor=Executors.newFixedThreadPool(4);// 提交任务executor.submit(()->System.out.println("Task running"));// 关闭executor.shutdown();

3. synchronized 基础 🔐

3.1 什么是 synchronized?

synchronized是 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:计数器privateintcount=0;publicsynchronizedvoidincrement(){count++;}publicsynchronizedintgetCount(){returncount;}// 场景 2:单例模式publicclassSingleton{privatestaticSingletoninstance;publicstaticsynchronizedSingletongetInstance(){if(instance==null){instance=newSingleton();}returninstance;}}// 场景 3:生产者-消费者publicclassBuffer{privateQueue<String>queue=newLinkedList<>();privateintcapacity=10;publicsynchronizedvoidput(Stringitem)throwsInterruptedException{while(queue.size()>=capacity){wait();// 等待消费者消费}queue.add(item);notifyAll();// 通知消费者}publicsynchronizedStringtake()throwsInterruptedException{while(queue.isEmpty()){wait();// 等待生产者生产}Stringitem=queue.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){// ...}

优点:避免线程阻塞
缺点:自旋消耗 CPU

4.5 重量级锁

多线程竞争激烈时,升级为重量级锁:

// 重量级锁:Monitor 机制// 未获取锁的线程阻塞,等待唤醒synchronized(this){// ...}

优点:竞争激烈时效率高
缺点:线程阻塞,切换开销大


5. 常见问题与注意事项 ⚠️

5.1 锁对象不能为 null

// ❌ 错误Objectlock=null;synchronized(lock){// NullPointerException}// ✅ 正确Objectlock=newObject();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 实例 synchronized

classUser{// 锁的是 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 排查死锁jstack<pid># 输出包含 "Found 1 deadlock."

小结

  • 线程六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
  • 线程创建:继承 Thread、实现 Runnable、实现 Callable、线程池
  • synchronized:保证原子性、可见性、可重入
  • 锁升级:偏向锁 → 轻量级锁 → 重量级锁
  • 注意事项:锁对象不能为 null、锁范围要适当、避免死锁

下一篇(027)预告volatile、happens-before 入门——可见性、指令重排、内存屏障、JMM 模型。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 19:47:48

基于Next.js全栈技术栈构建现代化健身应用实战解析

1. 项目概述&#xff1a;一个基于Next.js的现代化健身应用最近在梳理个人技术栈&#xff0c;想找一个能融合现代前端框架、全栈开发以及良好用户体验的实战项目。恰好&#xff0c;在GitHub上看到了mccmmj/nextjs-workout-app这个仓库。光看名字&#xff0c;一个基于Next.js的健…

作者头像 李华
网站建设 2026/4/27 19:42:22

业务接口脆弱性排查:杜绝恶意请求与低频渗透攻击

业务接口脆弱性排查方法输入验证与过滤 对所有接口输入进行严格验证&#xff0c;包括参数类型、长度、格式及业务逻辑合法性。采用白名单机制过滤特殊字符&#xff0c;防止SQL注入、XSS等攻击。对JSON/XML数据进行Schema校验&#xff0c;避免非法结构。频率限制与防重放 实施分…

作者头像 李华