使用synchronized获取互斥锁的几点说明

本文介绍了并发编程中互斥锁的概念及其实现原理。详细解释了如何通过synchronized关键字来实现互斥锁,保障线程安全。同时,讨论了不同情况下锁的行为表现及其对线程的影响。

转载请注明出处:http://blog.youkuaiyun.com/ns_code/article/details/17199201

 

    在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确保在某一时刻,方法内只允许有一个线程。

      采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。

 

      这里就使用同步机制获取互斥锁的情况,进行几点说明:


      1、如果同一个方法内同时有两个或更多线程,则每个线程有自己的局部变量拷贝。


      2、类的每个实例都有自己的对象级别锁。当一个线程访问实例对象中的synchronized同步代码块或同步方法时,该线程便获取了该实例的对象级别锁,其他线程这时如果要访问synchronized同步代码块或同步方法,便需要阻塞等待,直到前面的线程从同步代码块或方法中退出,释放掉了该对象级别锁。


      3、访问同一个类的不同实例对象中的同步代码块,不存在阻塞等待获取对象锁的问题,因为它们获取的是各自实例的对象级别锁,相互之间没有影响。


      4、持有一个对象级别锁不会阻止该线程被交换出来,也不会阻塞其他线程访问同一示例对象中的非synchronized代码。当一个线程A持有一个对象级别锁(即进入了synchronized修饰的代码块或方法中)时,线程也有可能被交换出去,此时线程B有可能获取执行该对象中代码的时间,但它只能执行非同步代码(没有用synchronized修饰),当执行到同步代码时,便会被阻塞,此时可能线程规划器又让A线程运行,A线程继续持有对象级别锁,当A线程退出同步代码时(即释放了对象级别锁),如果B线程此时再运行,便会获得该对象级别锁,从而执行synchronized中的代码。


     5、持有对象级别锁的线程会让其他线程阻塞在所有的synchronized代码外。例如,在一个类中有三个synchronized方法a,b,c,当线程A正在执行一个实例对象M中的方法a时,它便获得了该对象级别锁,那么其他的线程在执行同一实例对象(即对象M)中的代码时,便会在所有的synchronized方法处阻塞,即在方法a,b,c处都要被阻塞,等线程A释放掉对象级别锁时,其他的线程才可以去执行方法a,b或者c中的代码,从而获得该对象级别锁。


     6、使用synchronized(obj)同步语句块,可以获取指定对象上的对象级别锁。obj为对象的引用,如果获取了obj对象上的对象级别锁,在并发访问obj对象时时,便会在其synchronized代码处阻塞等待,直到获取到该obj对象的对象级别锁。当obj为this时,便是获取当前对象的对象级别锁。


     7、类级别锁被特定类的所有示例共享,它用于控制对static成员变量以及static方法的并发访问。具体用法与对象级别锁相似。


    8、互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。根据虚拟机规范的要求,在执行monitorenter指令时,首先要尝试获取对象的锁,如果获得了锁,把锁的计数器加1,相应地,在执行monitorexit指令时会将锁计数器减1,当计数器为0时,锁便被释放了。由于synchronized同步块对同一个线程是可重入的,因此一个线程可以多次获得同一个对象的互斥锁,同样,要释放相应次数的该互斥锁,才能最终释放掉该锁。

<think>好的,我现在需要回答用户关于同时使用两个互斥锁的问题。用户想知道在同一时间使用两个互斥锁是否可行,以及有什么限制。首先,我需要回顾一下互斥锁的基本概念和作用。互斥锁用于保护共享资源,防止多个线程同时访问导致数据不一致。 根据用户提供的引用[1],使用细粒度锁可以提升性能,允许不同的操作并行执行。比如,取款和修改密码使用不同的锁,这样它们就不会互相阻塞。这说明同时使用多个互斥锁是可行的,但需要注意资源之间的关联性。如果资源有关联,可能需要更谨慎的设计,避免死锁。 接下来,引用[2]和[3]提到synchronized关键字在Java中的使用。修饰静态方法和非静态方法时,锁定的对象不同。这说明锁的作用范围取决于锁定的对象,不同的锁可以保护不同的资源。但同时,如果一个资源被多个锁保护,可能会导致问题,因为每个资源应该由单一的锁来保护。 然后,我需要考虑使用多个锁可能带来的问题,比如死锁。当两个线程各自持有一个锁,并试图获取对方持有的锁时,就会发生死锁。因此,正确管理锁的获取顺序是关键。例如,可以规定所有线程必须按相同的顺序获取锁,或者使用超时机制来避免无限等待。 此外,引用[1]中提到“保护有关联关系的多个资源”时,可能需要更精细的锁策略。例如,银行转账需要同时锁定两个账户,这时必须确保所有线程以相同的顺序获取锁,防止死锁。 总结一下,同时使用两个互斥锁是可行的,但必须注意以下几点:1. 确保锁的粒度合适,避免不必要的串行化;2. 管理锁的获取顺序,防止死锁;3. 每个资源由单一锁保护,避免数据竞争。同时,需要根据具体场景设计锁策略,比如关联资源需要统一锁定顺序,非关联资源可以使用细粒度锁提高并发性。 最后,生成相关问题时要围绕多锁使用的情景、常见问题及解决方案展开,帮助用户深入理解。</think>在同一时间使用两个互斥锁是可行的,但需要满足特定条件并遵循规范以避免潜在问题。以下是具体分析: --- ### 一、可行性分析 1. **提升并发性能** 通过**细粒度锁**设计,可为不同资源分配独立锁。例如,银行账户系统中,取款操作和修改密码操作分别使用两把锁,使两者可并行执行,减少线程阻塞[^1]。 2. **保护关联资源** 当多个资源存在逻辑关联时(如转账需同时操作两个账户),需通过**统一加锁顺序**确保线程安全。例如: ```java synchronized (accountA) { synchronized (accountB) { // 转账逻辑 } } ``` --- ### 二、使用限制与风险 1. **死锁风险** 若两个线程以不同顺序获取锁,可能形成**循环等待**。例如: - 线程1先锁A后锁B - 线程2先锁B后锁A 此时若两线程同时执行,将导致死锁。 **解决方法**:全局规定锁的获取顺序(如按对象哈希值排序)。 2. **锁粒度控制** - **过度细化**:增加锁管理复杂度 - **过度粗化**:降低并发性能 需根据实际场景平衡,例如引用[2]中静态方法与非静态方法使用不同锁(类锁与实例锁)。 3. **锁泄露问题** 若未正确释放锁(如异常未处理),可能导致资源永久锁定。需通过`try-finally`确保解锁: ```java Lock lock1 = new ReentrantLock(); Lock lock2 = new ReentrantLock(); try { lock1.lock(); lock2.lock(); // 操作逻辑 } finally { lock2.unlock(); lock1.unlock(); } ``` --- ### 三、正确使用原则 | 原则 | 说明 | |---------------------|----------------------------------------------------------------------| | 单一职责锁 | 每把锁仅保护明确关联的资源[^2] | | 固定加锁顺序 | 所有线程按统一顺序获取锁(如按对象ID排序) | | 锁持有时间最小化 | 缩短锁的作用范围,减少竞争概率 | | 使用可中断/超时锁 | 通过`tryLock()`避免无限等待(如`ReentrantLock`支持超时机制) | --- ### 四、典型应用场景 1. **数据库连接池** 使用独立锁分别管理连接分配和状态更新。 2. **缓存系统** 读写锁分离(`ReadWriteLock`),允许多线程读、单线程写。 3. **GUI事件队列** 界面渲染与事件处理使用不同锁,避免交互阻塞。 --- 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值