同样是锁,先说说synchronized和lock的区别:

  1. synchronized是java关键字,是用c++实现的;而lock是用java类,用java可以实现
  2. synchronized可以锁住代码块,对象和类,但是线程从开始获取锁之后开发者不能进行控制和了解;lock则用起来非常灵活,提供了许多api可以让开发者去控制加锁和释放锁等等。

写个Demo

static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException { lock.lock();//其他没拿到锁的卡住不动 Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("start to get lock Interruptibly");
lock.unlock(); //看看会发生什么,注释掉再看看
lock.lock();
System.out.println("拿到锁");
lock.unlock();
System.out.println("释放锁");
}
});
thread.start(); Thread.sleep(3000);
lock.unlock();
}

我们自己来手写一下lock接口的tryLock()、lock()和unLock()方法,实现我们自己的myLock。

public class MyLock implements Lock {
//多并发调用 0-未占用 大于0-占用
AtomicInteger state = new AtomicInteger(); Thread ownerThread = new Thread(); //等待锁的队列
LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue(); @Override
public void lock() {
if (!tryLock()) { //先抢锁,所以是非公平锁
//没拿到锁,放到队列中去进行排队
waiters.add(Thread.currentThread());
//等待被唤醒
for (; ; ) {
if (tryLock()) { //非公平锁情况下,唤醒过来继续获取锁
waiters.poll(); //获取锁成功把自己从队列中取出来
return;
} else //获取锁失败
LockSupport.park(); //线程阻塞
}
}
} @Override
public boolean tryLock() {
if (state.get() == 0) { //如果锁没被占用
if (state.compareAndSet(0, 1)) { //如果成功拿到锁
ownerThread = Thread.currentThread(); //占用锁线程改为当前线程
return true;
}
}
return false;
} @Override
public void unlock() { if (ownerThread != Thread.currentThread()) //占用锁线程不是当前线程无法释放锁
throw new RuntimeException("非法调用,当前锁不属于你"); if (state.decrementAndGet() == 0) //如果成功释放锁
ownerThread = null; //占用锁线程置空
//通知其他线程
// Thread thread = null;
//
// while ((thread = waiters.peek()) != null)
// LockSupport.unpark(thread);
Thread thread = waiters.peek(); //获取队列头部线程,线程还留在队列中
if (thread != null) {
LockSupport.unpark(thread); //取消阻塞
}
} @Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
} @Override
public Condition newCondition() {
return null;
} @Override
public void lockInterruptibly() throws InterruptedException { }
}

几个注意点:

  • 锁的占用状态state是AtomicInteger类型,底层原理是CAS,这是为了保证在多并发情况下线程安全问题;
  • 当线程1释放锁成功时,获取队列头部线程但并不取出,因为非公平锁模式下,队列头部线程不一定能获取到锁;
  • LockSupport的park()和unPark()方法是native方法,可以阻塞,唤醒线程;

Lock默认是非公平锁,上面实现的也是非公平锁,小伙伴们可以试一试。

公平锁和非公平锁区别:

先等待先获取锁是公平锁;先等待也不一定先获取锁,可能被突然到来的线程获取到是非公平锁;

公平锁的实现:

  @Override
public void lock() {
checkQueue();//线程来的时候先不获取锁,而是先检查队列中有没有等待的线程,如果有,直接放入队列,如果没有,再去获取锁
if (!tryLock()) { //先抢锁,所以是非公平锁
//没拿到锁,放到队列中去进行排队
waiters.add(Thread.currentThread());
//等待被唤醒
for (; ; ) {
if (tryLock()) { //非公平锁情况下,唤醒过来继续获取锁
waiters.poll(); //获取锁成功把自己从队列中取出来
return;
} else //获取锁失败
LockSupport.park(); //线程阻塞
}
}
}

看完的小伙伴可以去看JDK提供的Lock源码啦。。

带你理解Lock锁原理的更多相关文章

  1. 通过实现网站访问计数器带你理解 轻量级锁CAS原理,还学不会算我输!!!

    一.实现网站访问计数器 1.线程不安全的做法 1.1.代码 package com.chentongwei.concurrency; import static java.lang.Thread.sl ...

  2. 手摸手带你理解Vue的Computed原理

    前言 computed 在 Vue 中是很常用的属性配置,它能够随着依赖属性的变化而变化,为我们带来很大便利.那么本文就来带大家全面理解 computed 的内部原理以及工作流程. 在这之前,希望你能 ...

  3. 手摸手带你理解Vue的Watch原理

    前言 watch 是由用户定义的数据监听,当监听的属性发生改变就会触发回调,这项配置在业务中是很常用.在面试时,也是必问知识点,一般会用作和 computed 进行比较. 那么本文就来带大家从源码理解 ...

  4. 一文带你看懂Java中的Lock锁底层AQS到底是如何实现的

    前言 相信大家对Java中的Lock锁应该不会陌生,比如ReentrantLock,锁主要是用来解决解决多线程运行访问共享资源时的线程安全问题.那你是不是很好奇,这些Lock锁api是如何实现的呢?本 ...

  5. 从原理剖析带你理解Stream

    摘要:Stream是jdk1.8给我们提供的新特性 本文分享自华为云社区<深入理解Stream之原理剖析>,作者: 李哥技术 . Stream是jdk1.8给我们提供的新特性,主要就是允许 ...

  6. 有关Java 锁原理

    锁 锁是用来锁东西的,让别人打不开也看不到!在线程中,用这个“锁”隐喻来说明一个线程在“操作”一个目标(如一个变量)的时候,如果变量是被锁住的,那么其他线程就对这个目标既“操作”不了(挂起)也无法看到 ...

  7. 解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度-美团)

    还有其他的锁,如果想要了解,参考:JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁, 用synchronized实现ReentrantLock 美团面试题参考:使用synchronized ...

  8. 锁之“轻量级锁”原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...

  9. java并发编程的艺术——第五章总结(Lock锁与队列同步器)

    Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...

随机推荐

  1. 【Vim命令】

    命令 操作 :set nu 显示行号  i 编辑模式  :wq  修改并退出  :%s/a/b  把所有的a替换成b                        

  2. SQL注入之常用工具sqlmap

    通常来说,验证一个页面是否存在注入漏洞比较简单,而要获取数据,扩大权限,则要输入很复杂的SQL语句,有时候我们还会对大量的URL进行测试,这时就需要用到工具来帮助我们进行注入了. 目前流行的注入工具有 ...

  3. vue全家桶(3.2)

    4.5.创建实例 可以创建一个自定义实例应用于多个请求接口 <template> <div class="page"> </div> </ ...

  4. HttPclient 以post方式发送json

    使用HttpClient 以POST的形式发送json字符串 步骤: 1.url .parameters 2.创建httpClient对象 3.创建HttpPost对象 4.为post对象设置参数 5 ...

  5. jQuery动态生成<select>下拉框

    前一阵在项目里需要动态生成下拉框,找了一下用jQuery实现比较方便,这里整理一下. 下文所述方法只是本人在项目中遇到问题的解决方法,场景较为简单,也希望能帮助有需要的朋友 1.动态生成下拉框的两种方 ...

  6. Spring笔记(3) - debug源码AOP原理解析

    案例 @EnableAspectJAutoProxy//开启基于注解的aop模式 @Configuration public class AOPConfig { //业务逻辑类加入容器中 @Bean ...

  7. 【PyMuPDF和pdf2image】Python将PDF转成图片

    前言: 在最近的测试中遇到一个与PDF相关的测试需求,其中有一个过程是将PDF转换成图片,然后对图片进行测试. 粗略的试了好几种方式,其中语言尝试了Python和Java,总体而言所找到的Python ...

  8. POJ3262贪心

    题意:FJ去砍树,然后和平时一样留了 N (2 ≤ N ≤ 100,000)头牛吃草.当他回来的时候,他发现奶牛们正在津津有味地吃着FJ种的美丽的花!为了减少后续伤害,FJ决定立即采取行动:运输每头牛 ...

  9. Linux系统安装JDK8

    一.卸载现用的JDK 1.查看Linux自带的JDK是否已安装 查看是否安装openjdk,java  -version (yum安装的 一般都是 OpenJDK     命令:yum install ...

  10. [JAVA]枚举类型的应用

    本文介绍枚举类的概念和开发过程中枚举的用法. 枚举类使用enum关键字定义,enum默认继承自Enum类,由于java单继承的特点,enum类无法再继承其他父类 一.枚举类的特性 1.简单枚举类的定义 ...