文章目录

  • 查看锁的信息
    • 饥饿死锁的例子
  • Synchronized 其 原 理 是 什 么 ?
  • 你 刚 才 提 到 获 取 对 象 的 锁 ,这 个“ 锁 ”到 底 是 什 么 ? 如 何 确 定 对象的锁
  • 什 么 是 可 重 入 性 , 为 什 么 说 Synchronized 是 可 重 入 锁 ?
  • 为 什 么 说 Synchronized 是 一 个 悲 观 锁 ? 乐 观 锁 的 实 现 原 理 又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?
  • 乐 观 锁 一 定 就 是 好 的 吗 ?
  • 跟 Synchronized 相 比 , 可 重 入 锁 ReentrantLock 其实现原理 有 什 么 不 同 ?
    • AQS框 架 是 怎 么 回 事 儿 ?
    • synchronized实例

查看锁的信息

jvisualvm
查看pid

死锁代码:


public class sisuoDemo4 {public static void main(String[] args) {Obj1 obj1 = new Obj1();Obj2 obj2 = new Obj2();Thread thread1 = new Thread(new SynAddRunalbe(obj1,obj2,1,2,true));Thread thread2 = new Thread(new SynAddRunalbe(obj1,obj2,2,1,false));thread1.setName("1");thread2.setName("线程2");thread1.start();thread2.start();}public static class SynAddRunalbe implements Runnable {Obj1 obj1;Obj2 obj2;int a,b;boolean flag;public SynAddRunalbe(Obj1 obj1, Obj2 obj2, int a, int b, boolean flag) {this.obj1 = obj1;this.obj2 = obj2;this.a = a;this.b = b;this.flag = flag;}@Overridepublic void run() {if(flag){synchronized (obj1){try {Thread.sleep(5);synchronized (obj2){System.out.println(a+b);System.out.println(Thread.currentThread().getName());}} catch (InterruptedException e) {e.printStackTrace();}}}else{synchronized (obj2)
{try{Thread.sleep(5);synchronized (obj1){System.out.println(a+b);System.out.println(Thread.currentThread().getName());}} catch (InterruptedException e) {e.printStackTrace();}
}}}}public static class Obj1 {}public static class Obj2 {}
}

C:\hello>jstack -l 20488




饥饿死锁的例子

public class ExecutorLock {private static ExecutorService single = Executors.newSingleThreadExecutor();
public static class AnotherCallable implements Callable<String> {@Override
public String call() throws Exception {System.out.println("in AnotherCallable");
return "annother success";
}
}public static class MyCallable implements Callable<String> {@Override
public String call() throws Exception {System.out.println("in MyCallable");
Future<String> submit = single.submit(new AnotherCallable());
return "success:" + submit.get();
}
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {MyCallable task = new MyCallable();
Future<String> submit = single.submit(task);
System.out.println(submit.get());
System.out.println("over");
single.shutdown();
}
}

Synchronized 其 原 理 是 什 么 ?

Synchronized 是 由 JVM 实 现 的 一 种 实 现 互 斥 同 步 的 一 种 方 式 , 如 果你查看被 Synchronized 修 饰 过 的 程 序 块 编 译 后 的 字 节 码 , 会 发 现 , 被Synchronized 修 饰 过 的 程 序 块 , 在 编 译 前 后 被 编 译 器 生 成了 monitorenter 和 monitorexit 两 个 字 节 码 指 令 。
这 两 个 指 令 是 什 么 意 思 呢 ?
在 虚 拟 机 执 行 到 monitorenter 指 令 时 , 首 先 要 尝 试 获 取 对 象 的 锁 :
如 果 这 个 对 象 没 有 锁 定 ,或 者 当 前 线 程 已 经 拥 有 了 这 个 对 象 的 锁 ,把 锁 的计数器 +1;当 执 行 monitorexit 指 令 时 将 锁 计 数 器 -1;当 计 数 器 为 0 时 , 锁 就 被 释 放 了 。
如 果 获 取 对 象 失 败 了 ,那 当 前 线 程 就 要 阻 塞 等 待 ,直 到 对 象 锁 被 另 外 一 个线 程 释 放 为 止 。
Java 中 Synchronize 通 过 在 对 象 头 设 置 标 记 , 达 到 了 获 取 锁 和 释 放 锁的目的。

你 刚 才 提 到 获 取 对 象 的 锁 ,这 个“ 锁 ”到 底 是 什 么 ? 如 何 确 定 对象的锁

“ 锁 ” 的 本 质 其 实 是 monitorenter 和 monitorexit 字 节 码 指 令 的 一 个
Reference 类 型 的 参 数 , 即 要 锁 定 和 解 锁 的 对 象 。 我 们 知 道 , 使 用Synchronized 可 以 修 饰 不 同 的 对 象 ,因 此 ,对 应 的 对 象 锁 可 以 这 么 确 定 。

  1. 如 果 Synchronized 明 确 指 定 了 锁 对 象 ,比 如 Synchronized( 变 量 名 )、Synchronized(this) 等 , 说 明 加 解 锁 对 象 为 该 对 象 。
  2. 如 果 没 有 明 确 指 定 :
    若 Synchronized 修 饰 的 方 法 为 非 静 态 方 法 ,表 示 此 方 法 对 应 的 对 象 为 锁对象;
    若 Synchronized 修 饰 的 方 法 为 静 态 方 法 ,则 表 示 此 方 法 对 应 的 类 对 象 为锁对象。
    注 意 , 当 一 个 对 象 被 锁 住 时 , 对象里面有用Synchronized 修饰的方法 都 将 产 生 堵 塞 , 而 对 象 里 非 Synchronized 修 饰 的 方 法 可 正 常 被 调 用 ,不 受 锁 影 响 。

什 么 是 可 重 入 性 , 为 什 么 说 Synchronized 是 可 重 入 锁 ?

可 重 入 性 是 锁 的 一 个 基 本 要 求 , 是 为 了 解 决 自 己 锁 死 自 己 的 情 况 。

比 如一 个 类 中 的 同 步 方 法 调 用 另 一 个 同 步 方 法 , 假 如
Synchronized 不 支 持 重 入 , 进 入 method2 方 法 时 当 前 线 程 获 得 锁 ,method2 方 法 里 面 执 行 method1 时 当 前 线 程 又 要 去 尝 试 获 取 锁 , 这时 如 果 不 支 持 重 入 , 它 就 要 等 释 放 , 把 自 己 阻 塞 , 导 致 自 己 锁 死 自 己 。

为 什 么 说 Synchronized 是 一 个 悲 观 锁 ? 乐 观 锁 的 实 现 原 理 又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?

Synchronized 显 然 是 一 个 悲 观 锁 , 因 为 它 的 并 发 策 略 是 悲 观 的 :
不 管 是 否 会 产 生 竞 争 ,任 何 的 数 据 操 作 都 必 须 要 加 锁 、用 户 态 核 心 态 转 换 、维 护 锁 计 数 器 和 检 查 是 否 有 被 阻 塞 的 线 程 需 要 被 唤 醒 等 操 作 。
随 着 硬 件 指 令 集 的 发 展 ,我 们 可 以 使 用 基 于 冲 突 检 测 的 乐 观 并 发 策 略 。先进 行 操 作 , 如 果 没 有 其 他 线 程 征 用 数 据 , 那 操 作 就 成 功 了 ;如 果 共 享 数 据 有 征 用 ,产 生 了 冲 突 ,那 就 再 进 行 其 他 的 补 偿 措 施 。这 种 乐观 的 并 发 策 略 的 许 多 实 现 不 需 要 线 程 挂 起 , 所 以 被 称 为 非 阻 塞 同 步 。
乐 观 锁 的 核 心 算 法 是 CAS( Compareand Swap,比较并交换 ) , 它 涉及 到 三 个 操 作 数 :内 存 值 、预 期 值 、新 值 。当 且 仅 当 预 期 值 和 内 存 值 相 等时才将内存值改为新值,这 样 处 理 的 逻 辑 是 , 首 先 检 查 某 块 内 存 的 值 是 否 跟 之 前 我 读 取 时 的 一 样 ,如 不 一 样 则 表 示 期 间 此 内 存 值 已 经 被 别 的 线 程 更 改 过 ,舍 弃 本 次 操 作 ,否
则 说 明 期 间 没 有 其 他 线 程 对 此 内 存 值 操 作 , 可 以 把 新 值 设 置 给 此 块 内 存 。
CAS 具 有 原 子 性 ,它 的 原 子 性 由 CPU 硬 件 指 令 实 现 保 证 ,即 使 用 JNI 调 用 Native 方 法 调 用 由 C++ 编 写 的 硬 件 级 别 指 令 , JDK 中 提 供 了Unsafe 类 执 行 这 些 操 作 。

乐 观 锁 一 定 就 是 好 的 吗 ?

乐 观 锁 避 免 了 悲 观 锁 独 占 对 象 的 现 象 ,同 时 也 提 高 了 并 发 性 能 ,但 它 也 有缺点:

  1. 乐 观 锁 只 能 保 证 一 个 共 享 变 量 的 原 子 操 作 。如 果 多 一 个 或 几 个 变 量 ,乐 观锁 将 变 得 力 不 从 心 ,但 互 斥 锁 能 轻 易 解 决 ,不 管 对 象 数 量 多 少 及 对 象 颗 粒度大小。
  2. 长 时 间 自 旋 可 能 导 致 开 销 大 。 假 如 CAS 长 时 间 不 成 功 而 一 直 自 旋 , 会给 CPU 带 来 很 大 的 开 销 。
  3. ABA 问题。CAS 的 核 心 思 想 是 通 过 比 对 内 存 值 与 预 期 值 是 否 一 样 而 判 断内 存 值 是 否 被 改 过 , 但 这 个 判 断 逻 辑 不 严 谨 , 假 如 内 存 值 原 来 是 A, 后来 被 一 条 线 程 改 为 B, 最 后 又 被 改 成 了 A, 则 CAS 认 为 此 内 存 值 并 没有 发 生 改 变 ,但 实 际 上 是 有 被 其 他 线 程 改 过 的 ,这 种 情 况 对 依 赖 过 程 值 的情 景 的 运 算 结 果 影 响 很 大 。解 决 的 思 路 是 引 入 版 本 号 ,每 次 变 量 更 新 都 把版 本 号 加 一 。

可重入锁 ReentrantLock 及 其他 显 式 锁相 关 问题

跟 Synchronized 相 比 , 可 重 入 锁 ReentrantLock 其实现原理 有 什 么 不 同 ?

其 实 , 锁 的 实 现 原 理 基 本 是 为 了 达 到 一 个 目 的 :
让 所 有 的 线 程 都 能 看 到 某 种 标 记 。
Synchronized 通过在对象头中设置标记实现了这一目的,是一种 JVM
原 生 的 锁 实 现 方 式 , 而 ReentrantLock 以 及 所 有 的 基 于 Lock 接 口 的实 现 类 ,都 是 通 过 用 一 个 volitile 修饰的 int 型 变 量 ,并 保 证 每 个 线 程都 能 拥 有 对 该 int 的 可 见 性 和 原 子 修 改 ,其 本 质 是 基 于 所 谓 的 AQS 框架 。

AQS框 架 是 怎 么 回 事 儿 ?

AQS( AbstractQueuedSynchronizer 类 ) 是 一 个 用 来 构 建 锁 和 同 步 器的 框 架 , 各 种Lock 包 中 的 锁 ( 常 用 的 有ReentrantLock 、ReadWriteLock) , 以 及 其 他 如 Semaphore、 CountDownLatch, 甚至 是 早 期 的 FutureTask 等 , 都 是 基 于 AQS 来构建。
5. AQS 在 内 部 定 义 了 一 个 volatile int state 变 量 , 表 示 同 步 状 态 : 当 线 程调 用 lock 方法时 ,如 果 state=0,说 明 没 有 任 何 线 程 占 有 共 享 资 源 的 锁 ,可 以 获 得 锁 并 将 state=1;如果 state=1, 则 说 明 有 线 程 目 前 正 在 使 用 共享 变 量 , 其 他 线 程 必 须 加 入 同 步 队 列 进 行 等 待 。
6. AQS 通 过 Node 内 部 类 构 成 的 一 个 双 向 链 表 结 构 的 同 步 队 列 , 来 完 成 线程 获 取 锁 的 排 队 工 作 , 当 有 线 程 获 取 锁 失 败 后 , 就 被 添 加 到 队 列 末 尾 。
Node 类 是 对 要 访 问 同 步 代 码 的 线 程 的 封 装 , 包 含 了 线 程 本 身 及 其 状 态 叫waitStatus( 有 五 种 不 同 取 值 , 分 别 表 示 是 否 被 阻 塞 , 是 否 等 待 唤 醒 , 是否 已 经 被 取 消 等 ) , 每 个 Node 结 点 关 联 其 prev 结点和 next 结点,方 便 线 程 释 放 锁 后 快 速 唤 醒 下 一 个 在 等 待 的 线 程 , 是 一 个 FIFO 的过程。
Node 类 有 两 个 常 量 , SHARED 和 EXCLUSIVE, 分 别 代 表 共 享 模 式 和 独占 模 式 。 所 谓 共 享 模 式 是 一 个 锁 允 许 多 条 线 程 同 时 操 作 ( 信 号 量Semaphore 就 是 基 于 AQS 的 共 享 模 式 实 现 的 ) , 独 占 模 式 是 同 一 个 时间 段 只 能 有 一 个 线 程 对 共 享 资 源 进 行 操 作 , 多 余 的 请 求 线 程 需 要 排 队 等 待( 如 ReentranLock) 。
7. AQS 通 过 内 部 类 ConditionObject 构 建 等 待 队 列 ( 可 有 多 个 ) , 当Condition 调 用 wait() 方 法 后 , 线程将会加入等待队列中 , 而当Condition 调 用 signal() 方 法 后 , 线 程 将 从 等 待 队 列 转 移 动 同 步 队 列 中进 行 锁 竞 争 。

synchronized实例

1.synchronized:内置的Java关键字
LOCK:一个接口,下面有多个实现类,可以判断是否取得了锁
2.synchronized自动释放锁,lock必须手动释放锁
3.synchronized不可以中断,非公平锁,lock 可以设置公平还是非公平
前者适合锁少量代码同步问题,后者适合锁大量同步代码
一个用synchronized的例子1:


public class A {public static void main(String[] args) {Threadtest threadtest = new Threadtest();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.increment();}catch (InterruptedException e){e.printStackTrace();}}},"A").start();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.decrement();}catch (InterruptedException e){e.printStackTrace();}}},"B").start();}
}

public class Threadtest
{private int number = 0;Lock lock = new ReentrantLock();public synchronized void increment()throws InterruptedException{// lock.lock();try{//自己的业务代码if(number !=0)this.wait();number++;System.out.println(Thread.currentThread().getName()+"->"+number);this.notify();}catch (Exception e){e.printStackTrace();}}public synchronized void decrement()throws InterruptedException{try{if(number == 0)this.wait();number--;System.out.println(Thread.currentThread().getName()+"->"+number);this.notify();}catch (Exception e){e.printStackTrace();}}
}

1this.notify()去掉this对结果无影响
以上例子如果再加一个线程C,改成notifyAll()会出现问题:
A->1
B->0
A->1
B->0
C->1
A->2
C->3
B->2
B->1
B->0
C->1
A->2

发现输出不正确了,是因为/if和while的虚假唤醒问题 ,改if为while
增加CD两个线程:


public class A {public static void main(String[] args) {Threadtest threadtest = new Threadtest();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.increment();}catch (InterruptedException e){e.printStackTrace();}}},"A").start();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.decrement();}catch (InterruptedException e){e.printStackTrace();}}},"B").start();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.increment();}catch (InterruptedException e){e.printStackTrace();}}},"C").start();new Thread(()->{for(int i = 0;i < 5;i++){try{threadtest.decrement();}catch (InterruptedException e){e.printStackTrace();}}},"D").start();}
}

public class Threadtest
{private int number = 0;Lock lock = new ReentrantLock();public synchronized void increment()throws InterruptedException{// lock.lock();try{//自己的业务代码while(number !=0)this.wait();number++;System.out.println(Thread.currentThread().getName()+"->"+number);notifyAll();}catch (Exception e){e.printStackTrace();}}public synchronized void decrement()throws InterruptedException{try{while (number == 0)this.wait();number--;System.out.println(Thread.currentThread().getName()+"->"+number);notifyAll();}catch (Exception e){e.printStackTrace();}}
}

输出
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0

五个为一轮

例子2:

public class Test {public static void main(String[] args) {Sell sell = new Sell();new Thread(() -> {sell.sell1();},"A").start();new Thread(() -> {sell.sell2();},"B").start();
}
}
class Sell{public synchronized void sell1(){System.out.println("卖衣服");}public synchronized void sell2(){System.out.println("卖包子");}
}

输出卖衣服
卖包子
如果sell2和sell1改一下顺序,则输出变成卖包子 卖衣服
说明:sychronized锁的对象是方法的调用者,由于上面两个方法用的是同一个锁,因此谁先拿到锁先执行谁
加了sleep方法:

public class Test {public static void main(String[] args) {Sell sell = new Sell();Sell mm = new Sell();new Thread(() -> {try {sell.sell2();} catch (InterruptedException e) {e.printStackTrace();}mm.sell1();},"A").start();new Thread(() -> {mm.sell1();try {mm.sell2();} catch (InterruptedException e) {e.printStackTrace();}},"B").start();
}
}
class Sell{public synchronized void sell1(){System.out.println("卖衣服"+Thread.currentThread().getName());}public synchronized void sell2() throws InterruptedException {Thread.sleep(2);System.out.println("卖包子"+Thread.currentThread().getName());}
}

卖衣服B
卖包子A
卖包子B
卖衣服A
记住:非同步方法不受锁的影响
如果sychronized修饰的方法是static的,则锁的是整个class,此时即使即使new了不同的两个实例,仍然是同一把锁

【学习笔记】juc并发学习+关于锁的面试题相关推荐

  1. JAVA学习笔记 -- JUC并发编程

    1 进程.线程 进程就是用来加载指令.管理内存.管理IO.当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程.进程可以被视为程序的一个实例. 线程,一个线程就是一个指令流,将指令流 ...

  2. 多线程编程学习笔记——使用并发集合(三)

    接上文 多线程编程学习笔记--使用并发集合(一) 接上文 多线程编程学习笔记--使用并发集合(二) 四.   使用ConcurrentBag创建一个可扩展的爬虫 本示例在多个独立的即可生产任务又可消费 ...

  3. Java学习笔记---多线程并发

    Java学习笔记---多线程并发 (一)认识线程和进程 (二)java中实现多线程的三种手段 [1]在java中实现多线程操作有三种手段: [2]为什么更推荐使用Runnable接口? [3][补充知 ...

  4. tornado学习笔记day01-高并发性能web框架

    tornado的安装 这里我使用的是虚拟环境中的pip安装,配合清华大学镜像源安装的 pip install tornado -i https://pypi.tuna.tsinghua.edu.cn/ ...

  5. 学习笔记-Java并发(一)

    学习笔记-Java并发(一) 目录 学习笔记-Java并发一 目录 Executer Callable和Future 后台线程 线程加入 小计 今天看了这一篇 Java编程思想-java中的并发(一) ...

  6. 深度学习入门之PyTorch学习笔记:深度学习介绍

    深度学习入门之PyTorch学习笔记:深度学习介绍 绪论 1 深度学习介绍 1.1 人工智能 1.2 数据挖掘.机器学习.深度学习 1.2.1 数据挖掘 1.2.2 机器学习 1.2.3 深度学习 第 ...

  7. 学习笔记:CentOS7学习之二十二: 结构化命令case和for、while循环

    目录 学习笔记:CentOS7学习之二十二: 结构化命令case和for.while循环 22.1 流程控制语句:case 22.2 循环语句 22.1.2 for-do-done 22.3 whil ...

  8. 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用

    目录 学习笔记:CentOS7学习之十六:LVM管理和ssm存储管理器使用 16.1 LVM的工作原理 16.1.1 LVM常用术语 16.1.2 LVM优点 16.2 创建LVM的基本步骤 16.2 ...

  9. 学习笔记:强化学习与最优控制(Chapter 2)

    Approximation in Value Space 学习笔记:强化学习与最优控制(Chapter 2) Approximation in Value Space 1. 综述 2. 基于Value ...

  10. 【长篇博文】Docker学习笔记与深度学习环境的搭建和部署(二)

    长篇博文记录学习流程不容易,请关注.转发.点赞.评论,谢谢! 上一篇文章:Docker学习笔记与深度学习环境的搭建和部署(一) 文章末尾附加nvidia455.23.cuda11.1.cudnn8.0 ...

最新文章

  1. 整合 Google 开源 C++ 代码
  2. 【SLAM】gradslam(∇SLAM)开源:论文、代码全都有
  3. git个人使用总结 —— idea命令行、撤销commit (未完待续)
  4. 当AV1视频编解码器来到Webex!
  5. 2018 CVPR GAN 相关论文调研
  6. CHANGE MASTER TO语法--MySql数据库
  7. layui移动开发_LayUI后台管理与综合示例
  8. HDFS 读/写数据流程
  9. 未找到插件 ‘org.springframework.bootspring-boot-maven-plugin‘(已解决 )
  10. webrtc在ubuntu14.04上的编译过程(12.04亦可)
  11. python 3d游戏脚本_3ds Max python脚本编写及部分API介绍
  12. Linux环境下安装yafu
  13. Flash Builder实用快捷键集锦
  14. HTML链接和锚点学习---第四天
  15. Mac上的GIF制作软件推荐
  16. 国家集训队论文分类整理
  17. 文件及文件的操作-读、写、追加的t和b模式
  18. nalu格式annex-B和avcc
  19. import * as 是什么?
  20. spring framework远程代码执行漏洞复现(CNVD-2022-23942 CVE-2022-22965)

热门文章

  1. 【数据结构与算法】常用算法
  2. 主曲率 matlab,基于Matlab的Hertz接触参数和主曲率差函数关系的拟合
  3. 黑金花大理石_黑色系大理石的首选——黑金花
  4. 文件流导出乱码_Savespss:不使用StatTransfer也可转换dta为sav文件的利器
  5. 全国计算机二级准考证贵州,贵州计算机二级考试准考证打印时间
  6. 二十七、 爬取Boss直聘的招聘信息
  7. 额外篇 | basemap(上)
  8. 后门怎么写隐蔽java_用Java写黑软-后门篇
  9. 直播 | NeurIPS 2021:基于投影变换建模传递关系的知识图谱表示学习
  10. 送书福利 | 浙江大学陈华钧教授新作,全面梳理知识图谱技术体系