类别 | synchronized | Lock |
层面 | JAVA 关键字,在JVM层面上 | 是一个接口类 |
锁的释放 | 1.正常情况下 执行完方法会释放锁; 2.异常情况 JVM 会自动释放锁 | 在finally中必须释放锁, 不然会产生死锁的情况 |
锁得获取 | 第一个线程 获取到锁,其他线程等待; 如果获取锁的线程阻塞,其他线程会一直等待; | 分情况而定,Lock有很多获取锁的方式, |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平 可非公平 |
性能 | 少量同步 | 大量同步 lock 可以提高多线程进行读操作的效率(可以通过readwritelock 实现读写分离) 在资源计征不是很激烈的情况下,Synchronized的性能要优于Reetrantlock 但是在资源竞争激烈的情况下Synchronized的性能会下降几十倍 ,但是ReetrantLock 会维持常态 |
使用场景 | 资源竞争少的情况下 | 资源竞争激烈的场景 |
synchronized 关键字 CPU悲观锁机制
synchronized 可以修饰在方法上 与代码块上;
被synchronized 修饰的方法不能被继承;
synchronized static 修饰的方法 是锁住的是类, 当一个类中 类方法有多个时 只要一个线程访问其中一个被 synchronized 修饰的类方法,其他线程就不能同时访问其他被 synchronized 修饰的类方法;
synchronized 修饰非static方法 锁住的是这个对象实例, 当一个对象实例中有多个 被synchronized 修饰的方法时, 只要其中一个方法被调用,其他方法就不能被访问; 例如 对象A 对象B 同属一个class 那么对象A调用自己的 synchronized 非static 方法不影响 对象B 调用自己的synchronized 方法 因为锁住的只是对象实例本身;
synchronized 修饰的代码块 锁住的是给定对象 例如 synchronized ( user.class ){ } 锁住的是整个类 ;synchronized (this){ } 锁住的这个对象本身; synchronized ( name ){ 锁住的是这个变量};
随意说: 一个面试中问道过 synchronized 可以调用被synchronized 修饰的方法吗?
synchroniezd 修饰的方法是可以调用被synchronized 修饰的方法 双重锁机制~
Lock 接口类 乐观锁
为什么会有Lock 这就要说说 synchroniezd 关键字的缺陷了; 被synchroniezd 所修饰的方法 一次锁住的是整个对象, 比如说 在一个类中 方法A写操作 方法B 读操作 当有一个线程作A方法时, 另一个线程请求方法B 时就只能等待A方法执行完才会进入B ,或者另一个中情况 当方法A 被操作是 时间过长 其他线程只能等待 这就会影响程序的执行效率! 然后就有了Lock可以不让很好的解决以上的问题;
Lock中声明了四种方法来获取锁, lock()方法 tryLock()方法 tryLock(long time, TimeUnit unit)方法 lockInterruptibly()方法 ; 释放锁的方法 是 unLock() lock不会自动释放锁;
lock() 是最常用的一个方法 用来获取锁, 如果锁被其他线程占用,其他线程则进行等待; 需要主动释放锁 所以代码逻辑必须在tyc{ 业务逻辑 }catch(Exception e){ 异常信息}finally{ lock.unLock() }中
tryLock():
Lock的类型
- 公平锁/非公平锁 公平锁:按照顺序获取锁,非公平锁:非顺序获取锁; synchronized 是一种非公平锁, lock可以通过构造函数指定该锁是否是公平锁 默认非公平锁, 非公平锁的吞吐量优于 公平锁的吞吐量两
- 可重入锁
- 独享锁/共享锁
- 互斥锁/读写锁 互斥锁 Reentrantlock 读写锁ReadWriteLock
- 乐观锁/悲观锁
- 分段锁: ConcurrentHashMap 使用了分段锁的设计理念,
- 偏向锁/轻量级锁/重量级锁
- 自旋锁