简要讨论java中的锁,synchronized和lock的区别

简要讨论java中的锁

synchronized和lock的区别是什么?synchronized到底锁的是什么?

当java代码中存在操作同一个共享资源的时候,共享资源可以是一个变量,一个对象,或者redis缓存中的同一个key对应的值,或者其他缓存数据数据库,关系型数据库中同一条记录,或者消息中间件中同一个消息等等,都可能出现线程安全的问题,为了解决这种问题,java提供了自己的锁机制来确保线程安全,最常见的就是synchronized和lock锁。

两者的区别:
1.synchronized是托管给JVM执行的,底层实现是c++调用的本地方法,而lock是一个对象,通过AQS(abstract queue schronized)抽象的队列同步器实现的,它维护一个变量的状态,提供了一系列cas操作。
2.synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。 
3.Lock用的是乐观锁方式。每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
4.ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。
5.ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
6.synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大。

ReentrantLock相对于synchronized多了三个高级功能:
1.等待可中断
在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待.

tryLock(long timeout, TimeUnit unit)

2.公平锁
按照申请锁的顺序来一次获得锁称为公平锁.synchronized的是非公平锁,ReentrantLock可以通过构造函数实现公平锁.

new RenentrantLock(boolean fair)

公平锁和非公平锁。这2种机制的意思从字面上也能了解个大概:即对于多线程来说,公平锁会依赖线程进来的顺序,后进来的线程后获得锁。而非公平锁的意思就是后进来的锁也可以和前边等待锁的线程同时竞争锁资源。对于效率来讲,当然是非公平锁效率更高,因为公平锁还要判断是不是线程队列的第一个才会让线程获得锁。

3.绑定多个Condition
通过多次newCondition可以获得多个Condition对象,可以简单的实现比较复杂的线程同步的功能.

详细的一些区别:
jdk1.6以前synchronized原始采用的是CPU悲观锁机制,它是个重量级锁,因为获取不到锁的线程会阻塞等待唤醒,这样会有线程的上下文切换,而线程的上下文切换由于涉及到操作系统内核态的转变,因此性能很低。
jdk1.6以后synchronized它变的轻量了,性能也好了很多,它是偏向锁-轻量级锁-重量级锁这样一步步进行锁升级的,而不是一上来就加个重量级锁。

当我们使用lock.lock()时,其内部主要是改变了一个变量状态,比如当两个线程使用lock获取锁时,比如下面的代码:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest{
   private static boolean isLocked;

   //lock方法
   public synchronized void lock() throws InterruptedException {
      if(isLocked){
         wait();
      }
      isLocked=true;
   }
   //unlock方法
   public synchronized void unLock(){
      isLocked=false;
      notify();
   }
}

这是一个最简单的不可重入锁的实现代码,线程1调用lock方法改变了变量的值为true,相当于上了一把锁,线程2这时候再来获取这把琐时就被挂起了,直到线程1调用unLock方法后唤起线程2释放锁,线程2才能继续执行。

特别注意,上面只是一个举例,真正java Lock接口下面的锁不是让没获取到锁的线程挂起,而是自旋等待,例如reentrantlock的实现是通过AQS(abstract queue schronized)抽象的队列同步器实现的,AQS用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。

synchronized它不是一个对象,是一个java关键字啊,那么大家有没有思考过synchronized到底改变了什么?比如下面这段代码

public class SynchronizedTest {
   private static final Object lock=new Object();
   private static int i;
   public void count(){
      synchronized(lock){
         i++;
      }
   }
}

synchronized(lock)到底改变了什么?
我们都知道synchronized是一个对象锁,那么synchronized到底改变了什么,要弄清楚这个问题必须先搞清楚java的对象布局。下图就是java的对象布局。

在这里插入图片描述
对象头里面存储了对象的锁状态,synchronized就是改变的对象头中锁的状态

<think>嗯,用户想了解Java中乐观悲观的概念及区别,包括它们的原理使用场景。首先,我得回忆一下这两种的基本定义。乐观悲观是并发控制中的两种不同策略,对吧? 根据引用[4],乐观假设并发冲突不常发生,所以只在提交操作时检查数据是否被修改。而悲观则相反,认为冲突很常见,所以在操作前就加,比如synchronizedReentrantLock。用户可能想知道它们具体怎么实现的,比如CAS机制版本号属于乐观,而synchronized是悲观的例子。 然后,原理部分需要详细解释。悲观的实现可能涉及到Java的内置机制,比如synchronized关键字或者Lock接口的实现类。乐观的实现通常用CAS操作,比如AtomicInteger这样的原子类,或者数据库中的版本号控制。引用[2]提到了用synchronizedAtomic来分别实现两种的例子,可以引用一下。 区别方面,用户可能关心性能、冲突处理使用场景。根据引用[3],乐观性能更好,因为它允许多个线程读取,只在写时检查冲突。而悲观在整个操作期间持有,避免了冲突但可能降低性能。适用场景的话,高并发读多用乐观,写多则用悲观。比如像数据库的更新操作,版本号机制适合乐观,而银行转账可能需要悲观确保安全。 使用场景需要具体举例。比如电商库存用乐观,避免频繁加影响性能;账户余额修改用悲观防止超额。这部分需要结合引用中的例子,确保准确。 用户可能的深层需求是选择适合自己项目的机制,所以需要明确各自的优缺点适用情况。可能还需要注意ABA问题,虽然用户没直接问,但提到CAS时应该简要说明。 最后,生成的相关问题要覆盖原理、区别、场景、性能比较Java中的具体实现,帮助用户进一步学习。需要确保引用正确,比如引用[1][3]提到性能比较,引用[4]解释了两者的基本区别。 总结时,要分点清晰,用例子帮助理解,同时引用对应的资料来支持观点,确保回答的准确性。</think>### Java 中乐观与悲观的对比分析 #### 1. **核心概念与原理** - **悲观** 假设并发冲突**必然发生**,因此在操作数据前**直接加**(如独占),确保操作期间数据不被其他线程修改。 **实现方式**:通过 `synchronized` 关键字或 `ReentrantLock` 显式实现[^2]。 **示例场景**:银行转账操作中,定账户余额以防止并发扣款冲突。 - **乐观** 假设并发冲突**较少发生**,仅在提交操作时检查数据是否被修改。若发生冲突,则通过重试或回滚处理。 **实现方式**: - **CAS(Compare and Swap)**:如 `AtomicInteger` 原子类直接修改内存值 - **版本号机制**:数据库通过版本号或时间戳标记数据修改[^4]。 **示例场景**:电商库存扣减,通过版本号验证避免超卖。 #### 2. **核心区别** | **维度** | **悲观** | **乐观** | |----------------|------------------------------|------------------------------| | **冲突处理** | 提前加,阻塞其他线程访问 | 提交时检查冲突,需重试或回滚 | | **性能开销** | 高(竞争频繁时易阻塞) | 低(无竞争时高效) | | **适用场景** | 写操作频繁、冲突概率高的场景 | 读操作频繁、冲突概率低的场景 | | **实现复杂度** | 简单(直接加) | 复杂(需处理冲突检测与重试) | #### 3. **使用场景对比** - **悲观适用场景** - 数据一致性要求**极高**(如金融交易) - 临界区代码执行**时间长**,冲突概率高[^3] - **乐观适用场景** - 读多写少(如缓存系统、统计计数) - 高并发场景需减少竞争(如秒杀库存)[^1] #### 4. **代码示例** - **悲观synchronized)** ```java public class PessimisticLock { private int balance = 100; public synchronized void transfer(int amount) { if (balance >= amount) { balance -= amount; } } } ``` - **乐观(Atomic类)** ```java public class OptimisticLock { private AtomicInteger stock = new AtomicInteger(100); public void deductStock() { int current; do { current = stock.get(); } while (!stock.compareAndSet(current, current - 1)); } } ``` #### 5. **注意事项** - **乐观的ABA问题**:CAS可能因值被其他线程修改后还原而产生误判,可通过**版本号递增**解决。 - **悲观的死风险**:需避免嵌套或超时机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值