Android多线程:Synchronized 解析

介绍

讲解悲观锁的工作原理,特点和作用以及和ReentrantLock的区别

一、 作用

保证同一时刻最多只有1个线程执行 被 Synchronized 修饰的方法 / 代码。保证线程安全,解决多线程中的同步问题

二、原理

  1. 依赖JVM来实现方法和代码块的同步
  2. 底层通过一个监视器对象monitor完成,wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出异常

三、 锁的类型

Synchronized 用于修饰代码块、类的实例方法 & 静态方法。分为对象锁和类锁。

  • 区别
类型 锁对象 锁数量 锁对象的内存区域
对象锁 实例对象 一个 栈
类锁 class 多个 方法区,所有线程共享,只有一份

四、线程竞争流程

JVM将锁信息保存在ObjectMonitor中,存储了两个队列entrySet和waitSet,分别用于存储竞争锁失败的和调用wait/sleep方法的阻塞线程。

尝试获取锁的线程会进入entry set,获取锁就会变成锁的owner,调用wait/sleep之后就会进入到wait set中,notify()唤醒wait set中的线程之后,对象会再次获取锁owner,如果失败再次进入到wait set,否则线程会继续执行,直到执行结束,释放锁。

五、 使用

对象锁

kotlin 复制代码
// 形式1:作用在非静态方法上
@Synchronized
fun start() {
   // do something
}

fun stop() {
    // 形式2:作用在非静态方法代码块中
    synchronized(this) {
        // do something
    }
}

类锁

kotlin 复制代码
companion object {
    // 形式1:作用在静态方法上
    @Synchronized
    fun stop() {
        // do something
    }
}

fun start() {
    // 形式2:作用在方法块上
    synchronized(Action::class.java) {
        // do something
    }
}

六、使用 ReentrantLock

5.1 特点

5.2 公平锁 & 非公平锁

scss 复制代码
public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

使用默认构造函数创建非公平锁。否则指定参数选择公平锁、非公平锁。

5.3 不可中断 & 可中断

  • lock()类似synchronized没有获取到锁会等待锁释放
kotlin 复制代码
fun testLock() {
    val lock = ReentrantLock()
    try {
        lock.lock() // 如果被其它资源锁定,会在此等待锁释放
        //do something
    } finally {
        lock.unlock()
    }
}
  • tryLock()在一段时间内尝试竞争锁。如果超时没有拿到锁,会直接退出竞争
kotlin 复制代码
fun testTryLock() {
    val lock = ReentrantLock()
    try {
        // 10s 内没有获取到锁返回 false
        if (lock.tryLock(10, TimeUnit.SECONDS)) {
            try {
                //do something
            } finally {
                lock.unlock()
            }
        }
    } catch (e: InterruptedException) {
        println("current thread is interrupted")
    }
}

七、synchronized和ReentrantLock比较

synchronized ReentrantLock
锁实现机制 依赖monitor 依赖AQS
锁类型 非公平锁 (任何一个等待的锁线程都有机会获得锁) 非公平锁 & 公平锁 (按照申请锁的时间依次获得该锁)
释放锁 自动释放(monitor) 手动调用unlock()
锁条件 一个 通过newCondition可以多个
可重入性 可 可

7.1 如何选择

当使用synchronized可以满足你的需求时,优先使用synchronized,因为使用方便,会自动释放锁。JDK1.6 中加入了很多针对锁的优化措施,在性能上两者基本是持平的,因此性能因素不再是选择ReentrantLock的理由了。如果你希望每个线程都有机会获得锁,并且可以预知你的系统是典型的高并发场景,选择ReentrantLock比较合适。

相关推荐
杨杨杨大侠8 小时前
都能接模型、RAG 和 MCP,Java Agent 框架到底差在哪?
java
Autumn_ing8 小时前
Design.md生成UI怎么验收?举例运营后台的颜色、状态和数据核对
前端·ui·ai·设计系统·paico·ai生成ui·design.md
keyipatience9 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
Thinker QAQ9 小时前
并发编程(九):BlockingQueue 与 Channel——语言层的原子性、可见性与有序性
java·python·go·并发编程·channel·blockingqueue·message passing
朱涛的自习室10 小时前
我给 Gemini 4 和 DeepSeek 搞了套「末位淘汰」,AI 干活会更猛吗?(Day 3)
android·前端·后端
霸道流氓气质11 小时前
RAG 可观测性与生产监控实战:四层遥测模型、五大仪表盘与 OpenTelemetry 全链路追踪的 Java 生产级实现
java·人工智能·python
AOI小白新手上路11 小时前
autodl常用的tar 打包下载(数据集)命令手册:逐段拆解与套用模板
java·大数据·数据库
java1234_小锋12 小时前
【技术专题】HTML5 网页技术 - HTML5简介 & HTML5 文档结构
前端·html·html5
鶴哥只手遮天12 小时前
Vue 3 核心源码深度剖析系列(v3.5.44)-第 1 章 项目全景:monorepo 架构与工程化体系
前端·vue.js·开源