JUC并发编程(常问的几个面试题)

目录

1.AQS的理解

2.lock和synchronized区别

3.什么叫做阻塞队列的有界和无界

4.CAS机制

5.怎么理解线程安全问题

6.什么是守护线程,它有什么特点


1.AQS的理解

AQS是多线程同步器,它是J.U.C包中多个组件的底层实现,如Lock、CountDownLatch、Semaphore等都用到了AQS.

从本质上来说,AQS提供了两种锁机制,分别是排它锁,和 共享锁。

排它锁,就是存在多线程竞争同一共享资源时,同一时刻只允许一个线程访问该共享资源,也就是多个线程中只能有一个线程获得锁资源,比如Lock中的ReentrantLock重入锁实现就是用到了AQS中的排它锁功能。

共享锁也称为读锁,就是在同一时刻允许多个线程同时获得锁资源,比如CountDownLatch和Semaphore都是用到了AQS中的共享锁功能。

2.lock和synchronized区别

  1. 从功能角度来看,Lock和Synchronized都是Java中用来解决线程安全问题的工具。
  2. 从特性来看,
    1. Synchronized是Java中的同步关键字,Lock是J.U.C包中提供的接口,这个接口有很多实现类,其中就包括ReentrantLock重入锁
    2. Synchronized可以通过两种方式来控制锁的粒度,(贴图)

一种是把synchronized关键字修饰在方法层面,

另一种是修饰在代码块上,并且我们可以通过Synchronized加锁对象的声明周期来控制锁的作用范围,比如锁对象是静态对象或者类对象,那么这个锁就是全局锁。

如果锁对象是普通实例对象,那这个锁的范围取决于这个实例的声明周期。

Lock锁的粒度是通过它里面提供的lock()unlock()方法决定的(贴图),包裹在这两个方法之间的代码能够保证线程安全性。而锁的作用域取决于Lock实例的生命周期

 

 

        3.Lock比Synchronized的灵活性更高,Lock可以自主决定什么时候加锁,什么时候释放锁,只需要调用lock()unlock()这两个方法就行,同时Lock还提供了非阻塞的竞争锁方法tryLock()方法,这个方法通过返回true/false来告诉当前线程是否已经有其他线程正在使用锁。

Synchronized由于是关键字,所以它无法实现非阻塞竞争锁的方法,另外,Synchronized锁的释放是被动的,就是当Synchronized同步代码块执行完以后或者代码出现异常时才会释放。

        4.Lock提供了公平锁和非公平锁的机制,公平锁是指线程竞争锁资源时,如果已经有其他线程正在排队等待锁释放,那么当前竞争锁资源的线程无法插队。而非公平锁,就是不管是否有线程在排队等待锁,它都会尝试去竞争一次锁。  Synchronized只提供了一种非公平锁的实现。

3.从性能方面来看,Synchronized和Lock在性能方面相差不大,在实现上会有一些区别,Synchronized引入了偏向锁、轻量级锁、重量级锁以及锁升级的方式来优化加锁的性能,而Lock中则用到了自旋锁的方式来实现性能优化。

3.什么叫做阻塞队列的有界和无界

  1. (如图),阻塞队列,是一种特殊的队列,它在普通队列的基础上提供了两个附加功能
    1. 当队列为空的时候,获取队列中元素的消费者线程会被阻塞,同时唤醒生产者线程。
    2. 当队列满了的时候,向队列中添加元素的生产者线程被阻塞,同时唤醒消费者线程。

     

 

  1. 其中,阻塞队列中能够容纳的元素个数,通常情况下是有界的,比如我们实例化一个ArrayBlockingList,可以在构造方法中传入一个整形的数字,表示这个基于数组的阻塞队列中能够容纳的元素个数。这种就是有界队列。
  2. 而无界队列,就是没有设置固定大小的队列,不过它并不是像我们理解的那种元素没有任何限制,而是它的元素存储量很大,像LinkedBlockingQueue,它的默认队列长度是Integer.Max_Value,所以我们感知不到它的长度限制。
  3. 无界队列存在比较大的潜在风险,如果在并发量较大的情况下,线程池中可以几乎无限制的添加任务,容易导致内存溢出的问题!

4.CAS机制

CAS是Java中Unsafe类里面的方法,它的全称是CompareAndSwap,比较并交换的意思。它的主要功能是能够保证在多线程环境下,对于共享变量的修改的原子性。

我来举个例子,比如说有这样一个场景(如图),有一个成员变量state,默认值是0,

定义了一个方法doSomething(),这个方法的逻辑是,判断state是否为0 ,如果为0,就修改成1。

这个逻辑看起来没有任何问题,但是在多线程环境下,会存在原子性的问题,因为这里是一个典型的,Read - Write的操作。

一般情况下,我们会在doSomething()这个方法上加同步锁来解决原子性问题。

但是,加同步锁,会带来性能上的损耗,所以,对于这类场景,我们就可以使用CAS机制来进行优化

这个是优化之后的代码(如图)

在doSomething()方法中,我们调用了unsafe类中的compareAndSwapInt()方法来达到同样的目的,这个方法有四个参数,

分别是:当前对象实例、成员变量state在内存地址中的偏移量、预期值0、期望更改之后的值1。

CAS机制会比较state内存地址偏移量对应的值和传入的预期值0是否相等,如果相等,就直接修改内存地址中state的值为1.

否则,返回false,表示修改失败,而这个过程是原子的,不会存在线程安全问题。

CompareAndSwap是一个native方法,实际上它最终还是会面临同样的问题,就是先从内存地址中读取state的值,然后去比较,最后再修改。

这个过程不管是在什么层面上实现,都会存在原子性问题。

所以呢,CompareAndSwap的底层实现中,在多核CPU环境下,会增加一个Lock指令对缓存或者总线加锁,从而保证比较并替换这两个指令的原子性。

CAS主要用在并发场景中,比较典型的使用场景有两个。

  1. 第一个是J.U.C里面Atomic的原子实现,比如AtomicInteger,AtomicLong。
  2. 第二个是实现多线程对共享资源竞争的互斥性质,比如在AQS、ConcurrentHashMap、ConcurrentLinkedQueue等都有用到。

5.怎么理解线程安全问题

所谓线程安全问题,简单来说,就是在多个线程访问某个方法或者对象的时候,不管通过任何的方式调用以及线程如何去交替执行。

在程序中不做任何同步干预操作的情况下,这个方法或者对象的执行/修改都能按照预期的结果来反馈。

我这样去解释,大家可能会有点懵逼。

实际上,线程安全问题的具体表现在三个方面,原子性、有序性、可见性。

原子性呢,是指当一个线程执行一系列程序指令操作的时候,它应该是不可中断的,因为一旦出现中断,站在多线程的视角来看,这一系列的程序指令会出现前后执行结果不一致的问题。

这个和数据库里面的原子性是一样的,就是一段程序只能由一个线程完整的执行完成,而不能存在多个线程干扰。

(如图)CPU的上下文切换,是导致原子性问题的核心,而JVM里面提供了Synchronized关键字来解决原子性问题。

可见性,就是说在多线程环境下,由于读和写是发生在不同的线程里面,有可能出现某个线程对共享变量的修改,对其他线程不是实时可见的。

导致可见性问题的原因有很多,比如CPU的高速缓存、CPU的指令重排序、编译器的指令重排序。

有序性,指的是程序编写的指令顺序和最终CPU运行的指令顺序可能出现不一致的现象,这种现象也可以称为指令重排序,所以有序性也会导致可见性问题。

可见性和有序性可以通过JVM里面提供了一个Volatile关键字来解决。

在我看来,导致有序性、原子性、可见性问题的本质,是计算机工程师为了最大化提升CPU利用率导致的。比如为了提升CPU利用率,设计了三级缓存、设计了StoreBuffer、设计了缓存行这种预读机制、在操作系统里面,设计了线程模型、在编译器里面,设计了编译器的深度优化机制。

今天的分享就到这里,在面试的时候大家还有遇到哪些比较难的问题,欢迎在评论区留言。

6.什么是守护线程,它有什么特点

下面我用最简单的方式让大家彻底搞懂守护线程。

简单来说,守护线程就是一种后台服务线程,他和我们在Java里面创建的用户线程是一模一样的。

守护线程和用户线程的区别有几个点,这几个点也是守护线程本身的特性:

  1. 在线程创建方面,对于守护线程,我们需要主动调用setDaemon()并且设置成true。
  2. 我们知道,一个Java进程中,只要有任何一个用户线程还在运行,那么这个java进程就不会结束,否则,这个程序才会终止。

注意,Java进程的终止与否,只和用户线程有关。如果当前还有守护线程正在运行,也不会阻止Java程序的终止。

因此,守护线程的生命周期依赖于用户线程。

举个例子,JVM垃圾回收线程就是一个典型的守护线程,它存在的意义是不断的处理用户线程运行过程中产生的内存垃圾。

一旦用户线程全部结束了,那垃圾回收线程也就没有存在的意义了。

由于守护线程的特性,所以它它适合用在一些后台的通用服务场景里面。

但是守护线程不能用在线程池或者一些IO任务的场景里面,因为一旦JVM退出之后,守护线程也会直接退出。

就会可能导致任务没有执行完或者资源没有正确释放的问题。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值