文章目录

  • 01、乐观锁和悲观锁
    • 悲观锁:
    • 乐观锁:
    • 小结
  • 02、公平锁和非公平锁
    • 公平锁
    • 非公平锁:
    • 小结
  • 03、 可重入锁
    • 概述
    • synchronized关键字
      • 演示可重入锁
      • 不需要手动释放锁
    • ReentrantLock
      • 演示可重入锁
    • ReentrantLock需要手动释放锁
  • 04、自旋锁
  • 05、死锁
    • 如何排查死锁
    • 如何解开死锁
  • 06、独占锁和共享锁
    • 独享锁
    • 共享锁
  • 07、互斥锁和读写锁

01、乐观锁和悲观锁

悲观锁:

  • 当线程去操作数据的时候,总认为别的线程会去修改数据,所以它每次拿数据的时候总会上锁,别的线程去拿数据的时候就会阻塞,比如synchronized

乐观锁:

  • 每次去拿数据的时候都认为别人不会修改,更新数据的时候会判断是别人是否回去更新数据,通过版本来判断,如果数据被修改了就拒绝更新,比如CAS是乐观锁,但严格来说并不是锁,通过原子性来保证数据的同步,比如说数据库的乐观锁,通过版本控制来实现,CAS不会保证线程同步,乐观的认为在数据更新期间没有其他线程影响
  • 注意:拿数据的时候不判断,更新数据的时候才会判断。

小结

  • 悲观锁适合写操作多的场景
  • 乐观锁适合读操作多的场景
  • 乐观锁的吞吐量会比悲观锁大

02、公平锁和非公平锁

公平锁

  • 指多个线程按照申请锁的顺序来获取锁
  • 公平锁: 非常公平, 不能够插队,必须先来后到!

非公平锁:

  • 获取锁的方式是随机获取的,保证不了每个线程都能拿到锁,也就是存在有线程饿死,一直拿不到锁,比如synchronized、ReentrantLock
  • 非公平锁:非常不公平,可以插队 (默认都是非公平锁)

小结

  • 非公平锁性能高于公平锁,更能重复利用CPU的时间
  • 参考可重入锁(ReentrantLock)的两个构造:一个可以传参,一个不可以传参,默认是非公平锁
public ReentrantLock() {//非公平锁:默认的sync = new NonfairSync();
}public ReentrantLock(boolean fair) {//true为公平锁,false为非公平锁sync = fair ? new FairSync() : new NonfairSync();
}

03、 可重入锁

概述

什么是可重入

可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁

可重入锁有

  • synchronized
  • ReentrantLock

synchronized关键字和ReentrantLock的区别:

synchronized不需要手动释放锁,而ReentrantLock需要手动释放锁

synchronized关键字

演示可重入锁

public class SynchronizedReentrantLock {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {synchronized (this){System.out.println("第1次获取锁,这个锁是:"+this);int index=1;while (true){synchronized (this){System.out.println("第"+(++index)+"次获取锁,这个锁是:"+this);}//重复获取十次锁if(index==10){break;}}}}}).start();}
}
  • 运行结果:可以重复的获取同一把锁,不出现死锁
第1次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第2次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第3次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第4次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第5次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第6次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第7次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第8次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第9次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b
第10次获取锁,这个锁是:com.aismall.somelocks.reentrantlock.SynchronizedReentrantLock$1@2f5aff2b

不需要手动释放锁

对于线程Thread_A:调用sendMsg方法的时候拿到一把锁,然后会自动拿到里面的call()方法的锁
等两个方法都执行完就释放锁,Thread_B拿到锁继续执行,不用手动释放锁

public class synchronizedLock {public static void main(String[] args) {Phone phone=new Phone();new Thread(()->{phone.sendMSG();},"Thread_A").start();new Thread(()->{phone.sendMSG();},"Thread_B").start();}
}class Phone{public  synchronized void sendMSG(){//这里有锁System.out.println(Thread.currentThread().getName()+" :sendMSG.....");call();//这里也有锁}public  synchronized void call(){System.out.println(Thread.currentThread().getName()+" :call.....");}
}
  • 运行结果
Thread_A :sendMSG.....
Thread_A :call.....
Thread_B :sendMSG.....
Thread_B :call.....

ReentrantLock

演示可重入锁

//加了多少次,解了多少次
public class ReentrantLockDemo {public static void main(String[] args) {//创建一个可重入锁ReentrantLock lock=new ReentrantLock();new Thread(new Runnable() {@Overridepublic void run() {try {//加锁lock.lock();System.out.println("第1次获取锁,这个锁是:" + lock);int index = 1;while (true) {try {//加锁lock.lock();System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);if (index == 10) {break;}} finally {//解锁System.out.println("第" + (index) + "次解锁,这个锁是:" + lock);lock.unlock();}}} finally {//解锁lock.unlock();System.out.println("第1"  + "次解锁,这个锁是:" + lock);}}}).start();}
}
  • 运行结果:加了多少次,解了多少次
第1次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第2次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第2次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第3次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第3次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第4次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第4次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第5次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第5次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第6次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第6次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第7次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第7次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第8次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第8次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第9次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第9次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第10次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第10次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Locked by thread Thread-0]
第1次解锁,这个锁是:java.util.concurrent.locks.ReentrantLock@749c37a6[Unlocked]

ReentrantLock需要手动释放锁

注意:拿锁和解锁的顺序相反,最后哪的最先解锁

Thread_A先拿到锁A,有拿到锁B,然后拿到锁C,如果这个三个锁有一个不解锁,Thread_B都不能执行,因为Thread_A拿锁和解锁的数量不一样,造成死锁。

public class ReentrantLockDemo  {public static void main(String[] args) {Phone02 phone02=new Phone02();new Thread(()->{phone02.sendMSG();},"Thread_A").start();new Thread(()->{phone02.sendMSG();},"Thread_B").start();}
}
class Phone02{Lock lock= new ReentrantLock();public void sendMSG(){// 细节问题:lock.lock(); lock.unlock()必须配对,否则就会死在里面lock.lock(); //锁Alock.lock();//锁Btry {System.out.println(Thread.currentThread().getName() + " sendMSG");call(); // 调用:锁C} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();//解锁Block.unlock();//解锁A}}public void call(){lock.lock();//锁Ctry {System.out.println(Thread.currentThread().getName() + "  call");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();//解锁C}}
}
  • 运行结果
Thread_A sendMSG
Thread_A  call
Thread_B sendMSG
Thread_B  call

04、自旋锁

自旋锁里面使用了CAS,java自带的元子类包中的很多类都是用CAS来实现自旋锁,例如java.util.concurrent.atomic. AtomicReference类中的很多方法都利用CAS实现了自旋锁

  • 例如这个方法:getAndUpdate
public final V getAndUpdate(UnaryOperator<V> updateFunction) {V prev, next;do {// 获取当前值,如果使用AtomicReference的空参构造,类型为传入的泛型,值为null,//使用AtomicReference的有参构造,类型为传入的泛型,值为形参值。prev = get();// 根据我们重写的apply方法获取到一个新的值next = updateFunction.apply(prev);//调用compareAndSet方法,如果我们获取的prev值不等于构造的时候传入的值//也就是在执行完构造方法之后,我们使用set方法更改了这个值,判断就会失败,//就会进入循环体,不对值进行更新,返回原始值,直到这个值为构造时传入的值才会替换//这就是自旋,一直等待,直到结果为true。} while (!compareAndSet(prev, next));return prev;}
  • 分析
- 这个方法的参数是一个UnaryOperator<V>类型的参数,如果不说一下这个参数什么意思,估计这段代码是看不懂的。- UnaryOperator<V>是一个函数式接口,这个接口继承自Function<T, R>接口,UnaryOperator意思是一元运算符
- 因为Function<T, R>接口是传入T类型的参数,返回R类型的值,如果传入的参数类型和返回的值类型相同,
- 就可以简化使用UnaryOperator<V>这个一元运算符接口。- Function<T, R>接口源码如下:我们只关注   R apply(T t);这个抽象方法@FunctionalInterfacepublic interface Function<T, R> {/*** Function接口中的抽象方法:传入一个T类型的参数,结果返回一个R类型的值*/R apply(T t);default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;}}-  UnaryOperator<V>接口源码如下:没什么好看的,我们可以看到Function<T, T>,也就是我们传入什么
- 类型的值,就返回什么类型的参数,我们使用这个接口要从写父类的抽象方法。@FunctionalInterfacepublic interface UnaryOperator<T> extends Function<T, T> {static <T> UnaryOperator<T> identity() {return t -> t;}}- 举例:
UnaryOperator<Integer> uop = x -> x + 1;
System.out.println(uop.apply(10)); // 11

我们也可以利用java自带的原子类来手动写一个自旋锁

public class MySpinLock {//使用 AtomicReference类,使用的是空参构造,所以初始化值为Thread类型的null值AtomicReference<Thread> atomicReference = new AtomicReference<>();// 加锁public void myLock(){Thread thread = Thread.currentThread();// 自旋锁:/*** 解析:因为创建atomicReference实例使用的是空参构造*      当创建对象的时候,默认为Thread类型的null*      第一个线程进来,利用空参构造创建一个对象,得到默认值null*      此时判断成立,将其替换为thread对象的地址,返回true,所以第一个*      进来的线程不会进入while循环*      第二个线程进来以后,由于默认值被替换为thread的地址不是null,所以进入while循环*      并且一直等待,直到第一个线程调用解锁方法,把这个thread的值替换为null,第二个线程才能执行*        */while (!atomicReference.compareAndSet(null,thread)){System.out.println(Thread.currentThread().getName() + "==> 拿不到锁");}}// 加锁public void myUnLock(){Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "==> 解锁");atomicReference.compareAndSet(thread,null);}public static void main(String[] args) {//创建一个锁MySpinLock lock=new MySpinLock();Thread thread01=new Thread(new Runnable() {@Overridepublic void run() {//加锁lock.myLock();System.out.println("thread01==> 拿到锁");try {//让线程睡1毫秒,看看线程thread02能不能拿到锁Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//解锁lock.myUnLock();}},"thread01");Thread thread02=new Thread(new Runnable() {@Overridepublic void run() {//加锁lock.myLock();System.out.println("thread01==> 拿到锁");//解锁lock.myUnLock();}},"thread02");thread01.start();thread02.start();}
}
  • 运行结果
thread01==> 拿到锁
thread02==> 拿不到锁
thread02==> 拿不到锁
thread02==> 拿不到锁
thread01==> 解锁
thread02==> 拿不到锁
thread01==> 拿到锁
thread02==> 解锁

05、死锁

死锁就是:

  • A线程拿着A锁,想要B锁,等待获取B锁
  • B线程拿着B锁,想要A锁,等待获取A锁
  • 两个线程相互等待,造成死锁

举个例子吧:

public class DeadLock {public static void main(String[] args) {String lockA="lockA";String lockB="lockB";//注意这个两个实例拿锁的顺序。。。。。MyDeadLockThread A=new MyDeadLockThread(lockA,lockB);MyDeadLockThread B=new MyDeadLockThread(lockB,lockA);new Thread(A).start();new Thread(B).start();}
}class MyDeadLockThread implements Runnable{private String lockA;private String lockB;public MyDeadLockThread(String lockA, String lockB) {this.lockA = lockA;this.lockB = lockB;}@Overridepublic void run() {synchronized (lockA){System.out.println(Thread.currentThread().getName() + ":拿到"+lockA+"=>想得到"+lockB);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB){System.out.println(Thread.currentThread().getName() + ":拿到"+lockB+"=>想得到"+lockA);}}}
}
  • 运行结果
Thread-0:拿到lockA=>想得到lockB
Thread-1:拿到lockB=>想得到lockA

如何排查死锁

先使用命令:jps -l查看到当前java进程的进程号

再使用命令:jstack 进程号查看当前线程的堆栈信息

如何解开死锁

方法:避免死锁产生的条件就可以了

得到这个答案的时候让我们想起了当初学红黑树的时,有人问怎么构建一个红黑树,只要满足红黑树的条件就是红黑树,为什么那,因为专家就是这样定义红黑树的,,,,

有些答案深入追究是没有意义的!!!!!

06、独占锁和共享锁

独享锁

  • 是指锁一次只能被一个线程持有。
  • 也叫X锁/排它锁/写锁/独享锁:该锁每一次只能被一个线程所持有,加锁后任何线程试图再次加锁的线程会被阻塞,直到当前线程解锁。例子:如果 线程A 对 data1 加上排他锁后,则其他线程不能再对 data1 加任何类型的锁,获得独享锁的线程即能读数据又能修改数据!

共享锁

  • 是指锁一次可以被多个线程持有。

  • 也叫S锁/读锁,能查看数据,但无法修改和删除数据的一种锁,加锁后其它用户可以并发读取、查询数据,但不能修改,增加,删除数据,该锁可被多个线程所持有,用于资源数据共享!

  • ReentrantLock和synchronized都是独享锁,ReadWriteLock的读锁是共享锁,写锁是独享锁。

07、互斥锁和读写锁

  • 与独享锁和共享锁的概念差不多,是独享锁和共享锁的具体实现。

  • ReentrantLock和synchronized都是互斥锁,ReadWriteLock是读写锁

JUC-08-java中的锁相关推荐

  1. 一文足以了解什么是 Java 中的锁

    作者 |  cxuan 责编 | Elle Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种类进行分类,下面是我对 Java 中一些常用锁的分类,包括一些基本的概述 从线程是否需要对资 ...

  2. Java中的锁原理、锁优化、CAS、AQS详解

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:景小财 www.jianshu.com/p/e674ee68 ...

  3. 一篇blog带你了解java中的锁

    前言 最近在复习锁这一块,对java中的锁进行整理,本文介绍各种锁,希望给大家带来帮助. Java的锁 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人 ...

  4. Java中的锁[原理、锁优化、CAS、AQS]

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:用好Java中的枚举,真的没有那么简单!个人原创+1博客:点击前往,查看更多 作者:高广超 链接:https:/ ...

  5. Java中的锁原理、锁优化、CAS、AQS详解!

    阅读本文大概需要 2.8 分钟. 来源:jianshu.com/p/e674ee68fd3f 一.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 二.锁实现的基本原理 2.1.v ...

  6. JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用

    转载自 http://blog.csdn.net/bigtree_3721/article/details/51296064 JAVA 中无锁的线程安全整数 AtomicInteger,一个提供原子操 ...

  7. 面试官让我讲讲Java中的锁,我笑了

    转载自  面试官让我讲讲Java中的锁,我笑了 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁 ...

  8. Java中的锁(转)

    Java中的锁 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的线程同步机制)是由synchronized同步 ...

  9. 关于Java中的锁,看这一篇就够了(总结篇)

    文章目录 锁的概念 锁的分类 一.什么是悲观锁? Java中的悲观锁有哪些 synchronized 基于AQS的实现类 二.什么是乐观锁? Java中的乐观锁有哪些 Valotile Java内存模 ...

  10. java中的锁(悲观锁、乐观锁、可重入锁、不可重入锁、公平锁、非公平锁、自旋锁、阻塞锁...)

    Lock接口 1.简介.地位.作用 ① 锁是一种工具,用于控制对共享资源的访问 ② Lock和synchronized,这两个是最常见的锁,它们都可以达到线程安全的目的,但是在使用和功能上又有较大的不 ...

最新文章

  1. Spring 泛型依赖注入
  2. linux重装系统后 70-persistent-net.rules 不能删除解决
  3. 多领导者改进算法的MATLAB仿真
  4. 坐标变换,空间变换的本质
  5. C语言再学习 -- 关闭/启动
  6. 【数据分析+建模案例】基于所给数据集对饭店流量指标进行预测
  7. textbox 和textera 文本框多行后不能拉伸
  8. 【数据库基础知识】数据库表格——主键和外键
  9. 古董笔记本伪装成双核 1G内存 120G硬盘
  10. Apache Flink 零基础入门(八): SQL 编程实践
  11. 两个excel表格信息合并 自动匹配_别再复制黏贴了,合并两个Excel表格可以很简单!...
  12. 思科网院Packet Tracer实验(三)调查运行中的 TCP/IP和OSI模型
  13. 【图像分割】基于matlab超像素图像分割【含Matlab源码 720期】
  14. 用思科模拟器对交换机进行超级终端配置和Telnet远程登录配置
  15. java blazeds_Flex+Java+Blazeds
  16. RBAC模型整合数据权限
  17. 开发一款游戏需要什么技术
  18. 斯坦福编程方法学作业讲解3---Karel机器人三大定律(上)
  19. 如何免费破解安装正版Adobe Photoshop CC2017
  20. Tengine + BabaSSL ,让国密更易用!

热门文章

  1. 【LVGL应用开发--基于STM32】第1章 初识LVGL
  2. 微服务拆分:业务横向拆分和纵向拆分
  3. “损失函数”是如何设计出来的?直观理解“最小二乘法”和“极大似然估计法”
  4. 程序员快速成长的核心原则
  5. 云宏广东省中医院虚拟化管理平台
  6. 微信jssdk上传图片无法预览wxLocalResource文件
  7. 带固态硬盘的电脑Ubuntu和Windows双系统安装
  8. scrapy爬虫没有任何的返回数据( Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min))
  9. pyltp的初始化报错:segmentor = Segmentor() # 初始化实例TypeError: __init__(): incompatible constructor argument
  10. 苦练基本功-计算机网络基础-TCP建立连接