Java高并发编程

本文介绍了volatile关键字的作用,内存屏障在多线程中的重要性,以及Synchronized锁的升级过程、轻量级锁和重量级锁的区别,还涵盖了乐观锁与悲观锁的概念,以及AQS框架在并发编程中的应用,包括同步等待队列和条件队列的使用。
摘要由CSDN通过智能技术生成

1、volatile

1.1、内存屏障是什么?volatile关键字会使本地内存失效,读取主内存的变量吗?

内存屏障就像是一个"壁垒",用于保证在这个"壁垒"之前和之后的操作顺序。

在多线程编程中,如果没有内存屏障,处理器可能会根据自己的优化策略进行指令的重排序,这可能导致一些问题,比如一个线程修改了一个共享变量的值,但其他线程看不到这个修改。

内存屏障就像是一个明确的标记点,它告诉处理器和编译器,在这个点之前的操作一定要先完成,然后才能继续执行后面的操作。这样可以确保操作的顺序和可见性,避免了因为指令重排而产生的问题,确保多线程程序的正确性和可靠性。

在这里插入图片描述

不完全准确。volatile关键字确实可以保证变量的可见性,也就是在多线程环境下,对一个被volatile修饰的变量的读取操作会直接读取主内存中的最新值,而不会使用线程的本地缓存。
当一个线程修改一个被volatile修饰的变量时,会立即将新值刷新到主内存中,而不是只在线程的本地缓存中进行修改。其他线程读取该变量时,会从主内存中重新获取最新值。
这种保证可见性的机制确实可以看作是使本地内存失效,因为对volatile变量的读取操作直接从主内存中获取,而不使用线程的本地缓存。这样可以确保多个线程之间对该变量的值的改动是可见的,避免了数据不一致的问题。

2、多线程

2.1、线程池执行的是run方法,而不是start方法吗?

在这里插入图片描述

2.2、多线程的run方法里要try catch ,不要throw,为什么?

在多线程编程中,如果在线程的run方法中抛出异常,并且没有适当地处理这些异常,会导致线程的终止和异常的丢失,从而可能对程序的稳定性和可靠性产生负面影响。因此,在run方法中通常建议使用try-catch块来捕获并处理异常,而不是将异常直接抛出(即不使用throws关键字)。

以下是几个原因:

  1. 终止线程问题:当一个线程抛出未捕获的异常时,该线程将被终止,并且可能会造成其他线程也受到影响。如果不适当地处理异常,线程的终止可能会导致应用程序的其他部分出现异常或无法正常运行。

  2. 异常丢失问题:如果在线程中抛出异常,但没有适当地处理它,那么异常可能会被错误地忽略或丢失。这样做会使得问题难以追踪和调试,并且很难定位和修复潜在的错误。

  3. 线程池问题:如果在使用线程池时,一个线程抛出异常且未被捕获,那么线程池中的其他线程可能会受到影响,影响整个应用程序的稳定性。

通过使用try-catch块来捕获并处理异常,可以增加程序的健壮性,避免异常的传播和丢失,保证程序的稳定性和可靠性。

3、锁

3.1、Synchronized锁升级过程及轻量级锁为什么要升级为重量级锁?

所谓锁,其实是在对象头里记录线程的id,只有被对象头记录的线程才会拥有锁。其中,当一个线程首次获取一个对象的锁时,JVM会将该线程的标识记录在对象头中,并将对象的锁标记设置为偏向锁。之后,该线程在获取该对象的锁时,无需进行任何同步操作,可以直接获得锁。这样可以减少互斥操作,提高单线程下的性能。如果出现多个线程竞争,偏向锁会先释放,然后升级为轻量级锁或重量级锁。偏向锁只涉及对象头的修改,不涉及操作系统层面的线程阻塞和唤醒。

误区: 无锁到偏向锁不能理解为锁升级,可以理解为无锁到偏向锁是从 0到1的过程。而升级则指的是本来就有锁,从1到∞的过程。

在这里插入图片描述

Synchronized的偏向锁是一种针对无竞争情况的锁优化机制。当一个线程首次获取一个对象的锁时,JVM会将该线程的标识记录在对象头中,并将对象的锁标记设置为偏向锁。之后,该线程在获取该对象的锁时,无需进行任何同步操作,可以直接获得锁。这样可以减少互斥操作,提高单线程下的性能。偏向锁只涉及对象头的修改,不涉及操作系统层面的线程阻塞和唤醒。同样的,轻量级锁(LightweightLock)是一种针对多线程竞争情况下的锁优化机制,它是在Java虚拟机层面实现的,而不是在操作系统层面。

3.1.1 偏向锁撤销&偏向锁解锁?
偏向锁的撤销是指在竞争发生时,将原本偏向锁所持有的状态转换为其他锁状态,如轻量级锁或重量级锁。解锁是指持有锁的线程执行完同步代码块或方法,释放锁,使其他线程可以竞争获取该锁。

3.2、乐观锁&悲观锁

乐观锁: 在更新数据时,通过比较当前状态和上一次的状态,来判断是否有其他线程修改了数据。如果没有冲突,就执行更新操作,否则就重试或者放弃。乐观锁的优点是减少了锁的开销,提高了并发性能;缺点是可能会造成大量的重试开销,以及存在ABA问题。

Synchronized 和ReentrantLock两者都是悲观锁,都支持可重入
可重入锁:重入是指线程可以多次获得对象的锁,而不会因为之前已经持有该锁而被阻塞。synchronized` 实现可重入的主要原因是由于每个锁关联一个持有者线程和计数器,当线程多次获取同一把锁时,计数器会递增,只有当计数器递减至零时锁才会被释放,保证了线程操作的正确性和一致性。ReentrantLock是state状态量,这种可重入的设计是为了解决类似于递归调用设计的。

在这里插入图片描述

4、AQS框架

AQS是java并发编程中将一些共性抽象出来的一个框架,提供了一套通用的接口和方法,开发者可以根据具体的需求来实现自己的同步器,以满足不同的并发场景,像我们java.util.concurrent包下的许多工具类都是基于此实现的,如下图:
在这里插入图片描述

AQS中的两种队列:

同步等待队列(双向链表): 主要用于维护获取锁失败时入队的线程
条件等待队列(单向链表): 调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获取锁

注意:同步等待队列中,会唤醒waitStatus=-1后面的线程节点。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值