当前位置:   article > 正文

JUC并发编程与源码分析学习笔记(二)

juc并发编程与源码分析

目录

二十九、多线程锁之线程锁知识概述

三十、多线程锁之悲观锁和乐观锁介绍

三十一、多线程锁之8锁案例编码演示

1、(代码)locks目录下:Lock8Demo.java

三十三、多线程锁之synchronized字节码分析

2、(代码)locks目录下:LockSyncDemo.java

三十四、多线程锁之synchronized底层原语分析

三十五、多线程之公平锁和非公平锁

三十六、多线程锁之可重入锁理论知识

三十七、多线程锁之可重入锁synchronized代码验证

3、(代码)locks目录下:ReEntryLockDemo.java

三十八、多线程锁之可重入锁原理分析和lock代码验证

三十九、多线程锁之死锁案例和排查命令

4、(代码)locks目录下:DeadLockDemo.java

四十、多线程锁之objectMonitor和synchronized锁小总结

四十一、中断机制之中断协商机制简介

四十二、中断机制之3大中断方法说明

四十三、中断机制之通过volatile实现线程中断停止

5、(代码)interrupt目录下:InterruptDemo.java

四十四、中断机制之通过AtomicBoolean实现线程中断停止

四十五、中断机制之通过interrupt实现线程中断停止

四十六、中断机制之interrupt和isInterrupted源码分析

四十七、中断机制之中断协商案例深度解析-上集

6、(代码)interrupt目录下:InterruptDemo2.java

四十八、中断机制之中断协商案例深度解析-下集

7、(代码)interrupt目录下:InterruptDemo3.java

四十九、中断机制之静态方法interrupted

8、(代码)interrupt目录下:InterruptDemo4.java

五十、LockSupport之是什么及等待唤醒机制对比

五十一、LockSupport之wait和notify实现等待和唤醒

9、(代码)LockSupport目录下:LockSupportDemo.java

五十二、LockSupport之await和signal实现等待和唤醒

五十三、LockSupport之park和unpark入门简介

五十四、LockSupport之park和unpark编码实战


二十九、多线程锁之线程锁知识概述

说说Java“锁”事

从轻松的乐观锁和悲观锁开讲

通过8种情况演示锁运行案例,看看我们到底锁的是什么

公平锁和非公平锁

可重入锁(又名递归锁)

死锁及排查

写锁(独占锁)/读锁(共享锁)

自旋锁SpinLock

无锁->独占锁->读写锁->邮戳锁

无锁->偏向锁->轻量锁->重量锁

大厂面试题复盘

并发编程高级面试解析

一、Synchronized相关问题

1、Synchronized用过吗,其原理是什么?

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

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

4、JVM对Java的原生锁做了哪些优化?

5、为什么说Synchronized是非公平锁?

6、什么是锁消除和锁粗化?

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

8、乐观锁一定就是好的吗?

二、可重入锁ReentrantLock及其他显示锁相关问题

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

2、那么请谈谈AQS框架是怎么回事儿?

3、请尽可能详尽地对比下Synchronized和ReentrantLock的异同。

4、ReentrantLock是如何实现可重入性的?

1、你怎么理解java多线程的?怎么处理并发?线程池有那几个核心参数?

2、Java加锁有哪几种锁?我先说了synchronized,刚讲到偏向锁,他就不让我讲了

3、简单说说lock

4、hashmap的实现原理?hash冲突怎么解决?为什么使用红黑树?

5、spring里面都使用了哪些设计模式?循环依赖怎么解决?

6、项目中哪个地方用了countdownlatch,怎么使用的?

三十、多线程锁之悲观锁和乐观锁介绍

悲观锁(狼性锁):认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改

synchronized关键字和Lock的实现类都是悲观锁

适合写操作多的场景,先加锁可以保证写操作时数据正确。

显示的锁定之后再操作同步资源

乐观锁(佛系锁):认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。

在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。

如果这个数据没有被更新,当前线程将自己修改的数据成功写入。

如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等

判断规则

1 版本号机制Version

2 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。

适合读操作多的常见,不加锁的特点能够使其读操作的性能大幅度提升。

乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是一句话:

乐观锁一般有两种实现方式

采用Version版本号机制

CAS(Compare-and-Swap,即比较并替换)算法实现

伪代码说明

三十一、多线程锁之8锁案例编码演示

【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。

1、(代码)locks目录下:Lock8Demo.java

  1. package com.nanjing.juc.locks;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 多线程锁之8锁案例编码演示
  5. *
  6. * @author xizheng
  7. * @date 2023-08-14 15:22:42
  8. */
  9. class Phone{
  10. public static synchronized void sendEmail(){
  11. try {
  12. TimeUnit.SECONDS.sleep(10);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. System.out.println("-----sendEmail");
  17. }
  18. public static synchronized void sendSMS(){
  19. System.out.println("-----sendSMS");
  20. }
  21. public void hello(){
  22. System.out.println("------hello");
  23. }
  24. }
  25. /**
  26. * 题目:谈谈你对多线程锁的理解,8锁案例说明
  27. * 口诀:线程 操作 资源类
  28. * 8锁案例说明:
  29. * 1 标准访问有ab两个线程,请问先打印邮件还是短信
  30. * 2 sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信
  31. * 3 添加一个普通的hello方法,请问先打印邮件还是hello
  32. * 4 有两部手机,请问先打印邮件还是短信
  33. * 5 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
  34. * 6 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
  35. * 7 有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信
  36. * 8 有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信
  37. *
  38. * 笔记总结:
  39. * 1-2
  40. * * * 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
  41. * * * 其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
  42. * * * 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
  43. * 3-4
  44. * * * 加个普通方法后发现和同步锁无关
  45. * * * 换成两个对象后,不是同一把锁了,情况立刻变化。
  46. *
  47. * 5-6 都换成静态同步方法后,情况又变化
  48. * 三种 synchronized 锁的内容有一些差别:
  49. * 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁->实例对象本身,
  50. * 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
  51. * 对于同步方法块,锁的是 synchronized括号内的对象
  52. *
  53. * 7-8
  54. * 当一个线程视图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
  55. * * *
  56. * * * 所有的普通同步方法用的都是同一把锁-实例对象本身,就是new出来的具体实例对象本身,本类this
  57. * * * 也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
  58. * * *
  59. * * * 所有的静态同步方法用的也是同一把锁-类对象本身,就是我们说过的唯一模板Class
  60. * * * 具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
  61. * * * 但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁才能获取锁。
  62. */
  63. public class Lock8Demo {
  64. public static void main(String[] args) {
  65. Phone phone = new Phone();
  66. Phone phone2 = new Phone();
  67. new Thread(() -> {
  68. phone.sendEmail();
  69. },"a").start();
  70. //暂停毫秒,保证a线程先启动
  71. try {
  72. TimeUnit.MILLISECONDS.sleep(200);
  73. } catch (InterruptedException e) {
  74. e.printStackTrace();
  75. }
  76. new Thread(() -> {
  77. // phone.sendSMS();
  78. // phone.hello();
  79. phone2.sendSMS();
  80. },"b").start();
  81. }
  82. }

三十三、多线程锁之synchronized字节码分析

synchronized有三种应用方式

8种锁的案例实际体现在3个地方

作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁;

作用于代码块,对括号里配置的对象加锁。

作用域静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁;

从字节码角度分析synchronized实现

javap -c ***.class文件反编译

synchronized同步代码块

javap -c .\LockSyncDemo.class(文件反编译)

synchronized同步代码块:实现使用的是monitorenter和monitorexit指令

synchronized普通同步方法

synchronized普通同步方法:调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会将先持有monitor锁,然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor

synchronized静态同步方法

ACC_STATIC,ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法

2、(代码)locks目录下:LockSyncDemo.java

  1. package com.nanjing.juc.locks;
  2. /**
  3. * 锁同步演示
  4. *
  5. * @author xizheng
  6. * @date 2023-08-14 16:45:14
  7. */
  8. public class LockSyncDemo {
  9. Object object = new Object();
  10. /**
  11. * monitorenter代表锁监视器获得了并进入
  12. *
  13. * 正常执行完毕以后会有
  14. * monitorexit说明要退出
  15. * synchronized的底层是依赖monitorenter和monitorexit来保证锁的获取和释放
  16. */
  17. public void m1(){
  18. synchronized (object){
  19. //也就是说这行代码,只要哪一个线程持有这把锁了,谁持有谁进来,进来以后干活,用完以后再将其释放
  20. System.out.println("----hello synchronized code block");
  21. //throw new RuntimeException("------exp");
  22. }
  23. }
  24. public synchronized void m2(){
  25. System.out.println("----hello synchronized m2");
  26. }
  27. public static synchronized void m3(){
  28. System.out.println("----hello static synchronized m3");
  29. }
  30. public static void main(String[] args) {
  31. }
  32. }

三十四、多线程锁之synchronized底层原语分析

反编译synchronized锁的是什么

面试题:为什么任何一个对象都可以成为一个锁

什么是管程monitor

大厂面试题讲解

【Java集合类】

1、从集合开始吧,介绍一下常用的集合类,哪些是有序的,哪些是无序的

2、hashmap是如何寻址的,哈希碰撞后是如何存储数据的,1.8后什么时候变成红黑树、说下红黑树,红黑树有什么好处

3、concurrenthashmap怎么实现线程安全,一个里面会有几个段segment,jdk1.8后有优化concurrenthashmap吗?分段锁有什么坏处

【多线程JUC】

4、reentrantlock实现原理,简单说下aqs

5、synchronized实现原理,monitor对象什么时候生成的?知道mnitor的monitorenter和monitorexit这两个是怎么保证同步的吗?或者说,这两个操作计算机底层是如何执行的

6、刚刚你提到了synchronized的优化过程,详细说明一下吧。偏向锁和轻量级锁有什么区别?

7、线程池几个参数说下,你们项目中如何根据实际场景设置参数的,为什么cpu密集设置的线程数集型少

什么是管程monitor

在HotSpot虚拟机中,monitor采用ObjectMonitor实现

上述C++源码解读

ObjectMonitor.java->ObjectMonitor.cpp->objectMonitor.hpp

objectMonitor.hpp

每个对象天生都带着一个对象监视器

每一个被锁住的对象都会和Monitor关联起来

提前剧透,混个眼熟

synchronized必须作用域某个对象中,所以Java在对象的头文件存储了锁的相关信息。锁升级功能主要依赖于MarkWord中的锁标志位和释放偏向锁标志位,后续讲解解锁升级时候我们再加深,目前为了承前启后的学习,对下图先混个眼熟即可

Hotspot的实现

三十五、多线程之公平锁和非公平锁

从ReentrantLock卖票demo演示公平锁和非公平现象

公平锁是指多个线程按照申请锁的顺序来获取锁,这里类似排队买票,先来的人先买后来的人在队尾排着,这是公平的 Lock lock = new ReentrantLock(true);//true 表示公平锁,先来先得
非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转或者饥饿的状态(某个线程一致得不到锁)

Lock lock = new ReentrantLock(false);//false 表示非公平锁,后来的也可能先获得锁

Lock lock = new ReentrantLock();//默认非公平锁

为什么会有公平锁和非公平锁的设计?为什么默认非公平?

1 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的,所以非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间。

2 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销。

三十六、多线程锁之可重入锁理论知识

可重入锁(又名递归锁)

是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象的是同一个对象),不会因为之前已经获取过还没释放而阻塞。

如果是1个有synchronized修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚。所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁。

一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入

自己可以获取自己的内部锁

三十七、多线程锁之可重入锁synchronized代码验证

可重入锁种类

隐式锁(即synchronized关键字使用的锁)默认是可重入锁

指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。

简单的来说就是:在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的

同步块

同步方法

Synchronized的重入的实现机理

显示锁(即Lock)也有ReentrantLock这样的可重入锁。

3、(代码)locks目录下:ReEntryLockDemo.java

  1. package com.nanjing.juc.locks;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * 可重入锁
  6. *
  7. * @author xizheng
  8. * @date 2023-08-16 09:54:48
  9. */
  10. public class ReEntryLockDemo {
  11. public synchronized void m1(){
  12. //指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。
  13. System.out.println(Thread.currentThread().getName()+"\t ----come in");
  14. m2();
  15. System.out.println(Thread.currentThread().getName()+"\t ----end in");
  16. }
  17. public synchronized void m2(){
  18. System.out.println(Thread.currentThread().getName()+"\t ----come in");
  19. m3();
  20. }
  21. public synchronized void m3(){
  22. System.out.println(Thread.currentThread().getName()+"\t ----come in");
  23. }
  24. static Lock lock = new ReentrantLock();
  25. public static void main(String[] args) {
  26. // ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
  27. //
  28. // new Thread(() -> {
  29. // reEntryLockDemo.m1();
  30. // },"t1").start();
  31. new Thread(() -> {
  32. lock.lock();
  33. try {
  34. System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
  35. lock.lock();
  36. try {
  37. System.out.println(Thread.currentThread().getName()+"\t ----come in内层调用");
  38. } finally {
  39. lock.unlock();
  40. }
  41. } finally {
  42. //由于加锁次数和释放次数不一样,第二个线程始终无法获取到锁,导致一直在等待。
  43. lock.unlock();//正常情况,加锁几次就要解锁几次
  44. }
  45. },"t1").start();
  46. new Thread(() -> {
  47. lock.lock();
  48. try {
  49. System.out.println(Thread.currentThread().getName()+"\t ----come in外层调用");
  50. } finally {
  51. lock.unlock();
  52. }
  53. },"t2").start();
  54. }
  55. public static void reEntryM1(String[] args) {
  56. final Object object = new Object();
  57. new Thread(() -> {
  58. synchronized (object){
  59. System.out.println(Thread.currentThread().getName() + "\t -----外层调用");
  60. synchronized (object){
  61. System.out.println(Thread.currentThread().getName() + "\t -----中层调用");
  62. synchronized (object){
  63. System.out.println(Thread.currentThread().getName() + "\t -----内层调用");
  64. }
  65. }
  66. }
  67. },"t1").start();
  68. }
  69. }

三十八、多线程锁之可重入锁原理分析和lock代码验证

Synchronized的重入的实现机理

每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针。

当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1。

在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁。

当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1。计数器为零代表锁已被释放。

为什么任何一个对象都可以成为一个锁

objectMonitor.hpp

三十九、多线程锁之死锁案例和排查命令

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

4、(代码)locks目录下:DeadLockDemo.java

  1. package com.nanjing.juc.locks;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 死锁
  5. *
  6. * @author xizheng
  7. * @date 2023-08-16 12:22:37
  8. */
  9. public class DeadLockDemo {
  10. public static void main(String[] args) {
  11. final Object objectA = new Object();
  12. final Object objectB = new Object();
  13. new Thread(() -> {
  14. synchronized (objectA){
  15. System.out.println(Thread.currentThread().getName()+"\t 自己持有A锁,希望获得B锁");
  16. try {
  17. TimeUnit.SECONDS.sleep(1);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. synchronized (objectB){
  22. System.out.println(Thread.currentThread().getName()+"\t 成功获得B锁");
  23. }
  24. }
  25. },"A").start();
  26. new Thread(() -> {
  27. synchronized (objectB){
  28. System.out.println(Thread.currentThread().getName()+"\t 自己持有B锁,希望获得A锁");
  29. try {
  30. TimeUnit.SECONDS.sleep(1);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. synchronized (objectA){
  35. System.out.println(Thread.currentThread().getName()+"\t 成功获得A锁");
  36. }
  37. }
  38. },"B").start();
  39. }
  40. }

排查命令(jstack 进程编号)

图形化:win+R 使用命令 jconsole

四十、多线程锁之objectMonitor和synchronized锁小总结

8锁案例运行,锁的到底是什么?对象锁、类锁

公平锁和非公平锁

可重入锁(又名递归锁)

死锁及排查

为什么任何一个对象都可以成为一个锁

ObjectMonitor中有几个关键属性

_owner指向持有ObjectMonitor对象的线程
_WaitSet存放处于wait状态的线程队列
_EntryList存放处于等待锁block状态的线程队列
_recursions锁的重入次数
_count用来记录该线程获取锁的次数

小总结(重要)

指针指向monitor对象(也称为管程或监视器锁)的起始地址。每个对象都存在着一个monitor与之关联,当一个monitor被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)

四十一、中断机制之中断协商机制简介

LockSupport与线程中断

线程中断机制

从阿里蚂蚁金服面试题讲起

void interrupt() 中断此线程
static boolean interrupted()测试当前线程是否已被中断
boolean isInterrupted()测试此线程是否已被中断

如何中断一个运行中的线程??

如何停止一个运行中的线程??

什么是中断机制

四十二、中断机制之3大中断方法说明

四十三、中断机制之通过volatile实现线程中断停止

大厂面试题中断机制考点

1、如何停止中断运行中的线程?

① 通过一个volatile变量实现

② 通过AtomicBoolean

③ 通过Thread类自带的中断api实例方法实现(在需要中断的线程中不断监听中断状态,一旦发生中断,就执行相应的中断处理业务逻辑stop线程)

2、当前线程的中断标识为true,是不是线程就立刻停止?

3、静态方法Thread.interrupted(),谈谈你的理解

5、(代码)interrupt目录下:InterruptDemo.java

  1. package com.nanjing.juc.interrupt;
  2. import java.util.concurrent.TimeUnit;
  3. import java.util.concurrent.atomic.AtomicBoolean;
  4. /**
  5. * 如何停止中断运行中的线程?
  6. *
  7. * @author xizheng
  8. * @date 2023-08-16 13:32:31
  9. */
  10. public class InterruptDemo {
  11. static volatile boolean isStop = false;
  12. static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
  13. public static void main(String[] args) {
  14. Thread t1 = new Thread(() -> {
  15. while (true){
  16. if(Thread.currentThread().isInterrupted()){
  17. System.out.println(Thread.currentThread().getName()+"\t isInterrupted()被修改为true,程序停止");
  18. break;
  19. }
  20. System.out.println("t1 -----hello interrupt api");
  21. }
  22. },"t1");
  23. t1.start();
  24. System.out.println("-----t1的默认中断标志位:"+t1.isInterrupted());
  25. //暂停毫秒
  26. try {
  27. TimeUnit.MILLISECONDS.sleep(20);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. //t2向t1发出协商,将t1的中断标志位设为true希望t1停下来
  32. new Thread(() -> {
  33. t1.interrupt();
  34. },"t2").start();
  35. }
  36. private static void m2_atomicBoolean(String[] args) {
  37. new Thread(() -> {
  38. while (true){
  39. if(atomicBoolean.get()){
  40. System.out.println(Thread.currentThread().getName()+"\t atomicBoolean被修改为true,程序停止");
  41. break;
  42. }
  43. System.out.println("t1 -----hello atomicBoolean");
  44. }
  45. },"t1").start();
  46. //暂停毫秒
  47. try {
  48. TimeUnit.MILLISECONDS.sleep(20);
  49. } catch (InterruptedException e) {
  50. e.printStackTrace();
  51. }
  52. new Thread(() -> {
  53. atomicBoolean.set(true);
  54. },"t2").start();
  55. }
  56. private static void m1_volatile(String[] args) {
  57. new Thread(() -> {
  58. while (true){
  59. if(isStop){
  60. System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序停止");
  61. break;
  62. }
  63. System.out.println("t1 -----hello volatile");
  64. }
  65. },"t1").start();
  66. //暂停毫秒
  67. try {
  68. TimeUnit.MILLISECONDS.sleep(20);
  69. } catch (InterruptedException e) {
  70. e.printStackTrace();
  71. }
  72. new Thread(() -> {
  73. isStop = true;
  74. },"t2").start();
  75. }
  76. }

四十四、中断机制之通过AtomicBoolean实现线程中断停止

代码如上

四十五、中断机制之通过interrupt实现线程中断停止

代码如上

四十六、中断机制之interrupt和isInterrupted源码分析

private native void interrupt0();

但凡有JVM的底层知识,native代表调用底层操作系统或者是第三方C语言的函数库

总结

四十七、中断机制之中断协商案例深度解析-上集

当前线程的中断标识为true,是不是线程就立刻停止?不会

6、(代码)interrupt目录下:InterruptDemo2.java

  1. package com.nanjing.juc.interrupt;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 当前线程的中断标识为true,是不是线程就立刻停止?
  5. *
  6. * @author xizheng
  7. * @date 2023-08-16 17:43:15
  8. */
  9. public class InterruptDemo2 {
  10. public static void main(String[] args) {
  11. //实例方法interrupt()仅仅是设置线程的中断状态位设置为true,不会停止线程
  12. Thread t1 = new Thread(() -> {
  13. for (int i = 1; i <= 300; i++) {
  14. System.out.println("-----: "+i);
  15. }
  16. System.out.println("t1线程调用interrupt()后的中断标识02: "+Thread.currentThread().isInterrupted());
  17. },"t1");
  18. t1.start();
  19. System.out.println("t1线程默认的中断标识: "+t1.isInterrupted());
  20. //暂停毫秒
  21. try {
  22. TimeUnit.MILLISECONDS.sleep(2);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. t1.interrupt();
  27. System.out.println("t1线程调用interrupt()后的中断标识01: "+t1.isInterrupted());
  28. try {
  29. TimeUnit.MILLISECONDS.sleep(2000);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. System.out.println("t1线程调用interrupt()后的中断标识03: "+t1.isInterrupted());//????---false中断不活动的线程不会产生任何影响。
  34. }
  35. }

四十八、中断机制之中断协商案例深度解析-下集

7、(代码)interrupt目录下:InterruptDemo3.java

  1. package com.nanjing.juc.interrupt;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * 中断demo3
  5. *
  6. * @author xizheng
  7. * @date 2023-08-16 17:54:22
  8. */
  9. public class InterruptDemo3 {
  10. public static void main(String[] args) {
  11. Thread t1 = new Thread(() -> {
  12. while (true){
  13. if(Thread.currentThread().isInterrupted()){
  14. System.out.println(Thread.currentThread().getName()+"\t "+
  15. "中断标志位: "+Thread.currentThread().isInterrupted()+" 程序停止");
  16. break;
  17. }
  18. try {
  19. Thread.sleep(200);
  20. } catch (InterruptedException e) {
  21. Thread.currentThread().interrupt();//为什么要在异常处,再调用一次??
  22. e.printStackTrace();
  23. }
  24. System.out.println("-----htllo InterruptDemo3");
  25. }
  26. },"t1");
  27. t1.start();
  28. //暂停几秒钟线程
  29. try {
  30. TimeUnit.SECONDS.sleep(1);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. }
  34. new Thread(() -> t1.interrupt(),"t2").start();
  35. }
  36. }
  37. /**
  38. * 1 中断标志位,默认false
  39. * 2 t2 ----> t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
  40. * 3 中断标志位true,正常情况,程序停止
  41. * 4 中断标志位true,异常情况下,InterruptedException,将会把中断状态将被清除,并且将收到InterruptedException。中断标志位false
  42. * 导致无限循环
  43. *
  44. * 5 在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才OK
  45. */

小总结:中断只是一种协商机制,修改中断标识仅此而已,不是立刻stop打断

四十九、中断机制之静态方法interrupted

private native boolean isInterrupted(boolean var1);

方法的注释也清晰的表达了“中断状态将会根据传入的ClearInterrupted”参数值确定是否重置

所以,

静态方法interrupted将   会清除中断状态(传入的参数ClearInterrupted为true)

实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)

8、(代码)interrupt目录下:InterruptDemo4.java

  1. package com.nanjing.juc.interrupt;
  2. import java.util.concurrent.locks.LockSupport;
  3. /**
  4. * 中断demo4
  5. *
  6. * @author xizheng
  7. * @date 2023-08-16 18:06:48
  8. */
  9. public class InterruptDemo4 {
  10. public static void main(String[] args) {
  11. //测试当前线程是否被中断 (检查中断标志),返回一个boolean并清除中断状态,
  12. // 第二次再调用时中断状态已经被清除,将返回一个false
  13. System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
  14. System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
  15. System.out.println("----1");
  16. Thread.currentThread().interrupt();//中断标志位设置为true
  17. System.out.println("----2");
  18. System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
  19. System.out.println(Thread.currentThread().getName()+"\t"+Thread.interrupted());
  20. //LockSupport.park();
  21. Thread.interrupted();//静态方法
  22. Thread.currentThread().isInterrupted();//实例方法
  23. }
  24. }

五十、LockSupport之是什么及等待唤醒机制对比

LockSupport是用来创建锁和其他同步类的基本线程阻塞原语

下面这句话,后面详细说

LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程

线程等待唤醒机制

1、3种让线程等待和唤醒的方法

①方式1:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程

②方式2:使用JUC包中Condition的await()方法让线程等待,使用signal()方法唤醒线程

③方式3:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

五十一、LockSupport之wait和notify实现等待和唤醒

异常1:wait方法和notify方法,两个都去掉同步代码块

异常2:将notify放在wait方法前面,程序无法执行,无法唤醒

小总结:wait和notify方法必须要在同步块或者方法里面,且成对出现使用

先wait后notify才OK

9、(代码)LockSupport目录下:LockSupportDemo.java

  1. package com.nanjing.juc.LockSupport;
  2. import java.util.concurrent.TimeUnit;
  3. import java.util.concurrent.locks.Condition;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.LockSupport;
  6. import java.util.concurrent.locks.ReentrantLock;
  7. /**
  8. * @author xizheng
  9. * @date 2024-01-25 15:24:12
  10. */
  11. public class LockSupportDemo {
  12. static int x = 0;
  13. static int y = 0;
  14. public static void main(String[] args)
  15. {
  16. Thread t1 = new Thread(() -> {
  17. try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
  18. System.out.println(Thread.currentThread().getName() + "\t ----come in"+System.currentTimeMillis());
  19. LockSupport.park();
  20. System.out.println(Thread.currentThread().getName() + "\t ----被唤醒"+System.currentTimeMillis());
  21. }, "t1");
  22. t1.start();
  23. //暂停几秒钟线程
  24. //try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  25. new Thread(() -> {
  26. LockSupport.unpark(t1);
  27. System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
  28. },"t2").start();
  29. }
  30. private static void lockAwaitSignal()
  31. {
  32. Lock lock = new ReentrantLock();
  33. Condition condition = lock.newCondition();
  34. new Thread(() -> {
  35. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  36. lock.lock();
  37. try
  38. {
  39. System.out.println(Thread.currentThread().getName()+"\t ----come in");
  40. condition.await();
  41. System.out.println(Thread.currentThread().getName()+"\t ----被唤醒");
  42. } catch (InterruptedException e) {
  43. e.printStackTrace();
  44. } finally {
  45. lock.unlock();
  46. }
  47. },"t1").start();
  48. //暂停几秒钟线程
  49. //try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  50. new Thread(() -> {
  51. lock.lock();
  52. try
  53. {
  54. condition.signal();
  55. System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
  56. }finally {
  57. lock.unlock();
  58. }
  59. },"t2").start();
  60. }
  61. private static void syncWaitNotify()
  62. {
  63. Object objectLock = new Object();
  64. new Thread(() -> {
  65. try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  66. synchronized (objectLock){
  67. System.out.println(Thread.currentThread().getName()+"\t ----come in");
  68. try {
  69. objectLock.wait();
  70. } catch (InterruptedException e) {
  71. e.printStackTrace();
  72. }
  73. System.out.println(Thread.currentThread().getName()+"\t ----被唤醒");
  74. }
  75. },"t1").start();
  76. //暂停几秒钟线程
  77. //try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
  78. new Thread(() -> {
  79. synchronized (objectLock){
  80. objectLock.notify();
  81. System.out.println(Thread.currentThread().getName()+"\t ----发出通知");
  82. }
  83. },"t2").start();
  84. }
  85. }

五十二、LockSupport之await和signal实现等待和唤醒

结论:在lock、unlock对里面,才能正确调用condition中线程等待和唤醒的方法

小总结:Condition中的线程等待和唤醒方法,需要先获取锁。一定要先await后signal,不要反了

上述两个对象Object和Condition使用的限制条件

①、线程先要获得并持有锁,必须在锁块(synchronized或lock)中

②、必须要先等待后唤醒,线程才能够被唤醒

五十三、LockSupport之park和unpark入门简介

LockSupport类中的park等待和unpark唤醒

LockSupporticon-default.png?t=N7T8https://www.runoob.com/manual/jdk11api/java.base/java/util/concurrent/locks/LockSupport.html1、是什么?

通过park()和unpark(thread)方法来实现阻塞和唤醒线程的操作

2、主要方法

①、阻塞 park() / park(Object blocker)

阻塞当前线程/阻塞传入的具体线程

permit许可证默认没有不能放行,所以一开始调park()方法当前线程就会阻塞,直到别的线程给当前线程的发放permit,park方法才会被唤醒。

②、唤醒 unpark(Thread thread)

唤醒处于阻塞状态的指定线程

调用unpark(thread)方法后,就会将thread线程的许可证permit发放,会自动唤醒park线程,即之前阻塞中的LockSupport.park()方法会立即返回。

五十四、LockSupport之park和unpark编码实战

1、代码

正常+无锁块要求

之前错误的先唤醒后等待,LockSupport照样支持

重点说明(重要)

面试题

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/966795
推荐阅读
相关标签
  

闽ICP备14008679号