IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Lock与Synchronized区别 -> 正文阅读

[Java知识库]Lock与Synchronized区别

先说结论,后面详解

  1. synchronized是关键字,Lock是接口;

  2. synchronized是隐式的加锁,lock是显式的加锁;

  3. synchronized可以作用于方法上,lock只能作用于方法块;

  4. synchronized底层采用的是objectMonitor,lock采用的AQS;

  5. synchronized是阻塞式加锁,lock是非阻塞式加锁支持可中断式加锁,支持超时时间的加锁;

  6. synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列, lock有一个同步队列,可以有多个等待队列;

  7. synchronized只支持非公平锁,lock支持非公平锁和公平锁;

  8. synchronized使用了object类的wait和notify进行等待和唤醒, lock使用了condition接口进行等待和唤醒(await和signal);

  9. lock支持个性化定制, 使用了模板方法模式,可以自行实现lock方法;

synchronized是关键字,Lock是接口

// lock接口的方法
void lock();
void unlock();
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void lockInterruptibly() throws InterruptedException;
Condition newCondition();

synchronized是隐式的加锁,lock是显式的加锁

// synchronized 作用于方法上时,是看不到有锁的释放的
// lock 作用于代码块, 经常是 try lock finally unlock 一起使用, 是有主动去加锁和解锁的

synchronized可以作用于方法上,lock只能作用于方法块

// synchronized作用于静态方法和普通方法的区别是?
1.作用于静态方法时,锁的对象是class
2.作用于普通方法是,锁的可以是任意的对象  
3.作用于代码块时,反编译后,monitorenter  monitorexit  exception时的monitorexit
4.作用于方法时,反编译后,方法的flag里面加一个acc_synchronized访问标志    
// lock 作用于代码块, 经常是 try lock finally unlock 一起使用    

synchronized是阻塞式加锁,lock是非阻塞式加锁支持可中断式加锁,支持超时时间的加锁

// lock更加灵活,提供了多种方式的加锁方法
tryLock(long time, TimeUnit unit) // 带超时时间的加锁方法
lockInterruptibly // 可中断加锁方法

synchronized底层采用的是objectMonitor,lock采用的AQS

// objectMonitor
A,B线程竞争获取锁时,加入B线程未获取到锁,会进入到objectMonitor的entrylist(同步队列),获取到锁的线程调用wait后会进入waitset(等待队列)
// lock的AQS
通过一个int类型的state变量,来判断锁是否被获取,未获取到锁的线程会通过cas加入到双端队列的尾部    

synchronized在进行加锁解锁时,只有一个同步队列和一个等待队列, lock有一个同步队列,可以有多个等待队列

// lock中可以有多个等待队列 condition

synchronized只支持非公平锁,lock支持非公平锁和公平锁

// lock 支持非公平锁和公平锁, 默认创建是非公平锁
公平锁与非公平锁的区别就在于获取锁的方式不同,公平锁获取,当前线程必须检查sync queue里面是否已经有排队线程。而非公平锁则不用考虑这一点,当前线程可以直接去获取锁。
// 非公平锁可能出现线程饥饿问题

synchronized使用了object类的wait和notify进行等待和唤醒, lock使用了condition接口进行等待和唤醒(await和signal)

// condition 用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。
    
// synchronized wait()只有一个阻塞队列,notifyAll会唤起所有阻塞队列下的线程,而使用lock/condition,可以实现多个阻塞队列,signalAll只会唤起某个阻塞队列下的阻塞线程。

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length)
         notFull.await();
       items[putptr] = x;
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signalAll();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0)
         notEmpty.await();
       Object x = items[takeptr];
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signalAll();
       return x;
     } finally {
       lock.unlock();
     }
   }
 }
/* 上面代码中线程间共享的资源是一个Oject数组items,因为需要对其进行并发操作,所以显然需要制定同步策略。
  对容器的基本操作无非就是增删改查,但是显然我们不能只是简单的增,简单的删,因为我们得考虑边界情况,数组有长度,增只有在容器还有空间的情况下才能增;删除则只有在容器有元素的情况下才能减。于是在put方法中我们得先判断一下是否还有位置才能对容器进行add操作;在take方法中我们得先判断是否有元素才能进行remove操作,而且这个判断逻辑是无法避免的。执行判断逻辑的时候我们已经获得了锁(能不能在实际add或者remove的时候再加锁,显然不能,因为判断逻辑的实现依赖共享资源items,所以必须在判断逻辑之前已经获得锁),如果判断逻辑为false我们只能先释放已经获得的锁并且等待条件满足再继续获取锁执行(这也是Condition中await方法的作用)。
 */     

// 为什么需要使用Condition(什么时候需要使用await/signalAll/signal方法)?
   /*  
   因为有时候获得锁的线程发现其某个条件不满足导致不能继续后面的业务逻辑,此时该线程只能先释放锁,等待条件满足。那可不可以不释放锁的等待呢?比如将await方法替换为sleep方法(这也是面试经常问的await和sleep的区别)?
显然不行,因为等待的条件显然和共享的资源是有关的,在这个例子里,take方法会等待notEmpty条件,notEmpty指的是items不为空,意味着此时items是空的,那么就只有对items执行add操作,即其它线程调用put方法才有机会达到notEmpty的条件,所以如果使用sleep(不释放锁)来等待而不是await(释放锁)来等待,则会导致notEmpty这个条件永远满足不了。
    总结起来,就是获得锁的线程发现某个条件不满足而不能继续执行,而且该条件需要其它线程对共享资源进行操作才能触发,所以必须释放锁。
*/      

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-17 21:56:42  更:2022-03-17 21:59:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:58:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码