并发编程面试题(补充,高频实战面试)

AQS 的理解

第一 : AQS 是AbstractQueuedSynchronizer的简称,抽象同步队列,是并发编程比较核心的组件,是多线程
       同步器,是JUC包下多个组件的底层实现,比如 Lock 闭锁 信号量等

第二 ; 本质讲,AQS提供两种锁机制,一排他,二共享锁
    排他 : 多线程竞争同一个共享资源时,同一时刻只允许一个线程访问该共享资源,最后只有一个线程获得锁
	资源,比如 ReetrantLock重入锁就是用到AQS的排他功能;
	共享锁 : 也称读锁,同一时刻允许多线程同时获得锁资源,比如闭锁信号量都是用AQS的共享锁

第三 : AQS 底层的关键是一个state 和一个双向队列以及CAS算法实现;
	每个节点代表一个线程,节点有两个属性,一是状态state,二是线程thread,前者表示该线程是否拿到锁
        或等待队列有多少线程正在等待,线程属性用来记录该节点对应的线程;
	双向链表存储等待队列的节点,节点都有一个前驱和后继;当一个线程想获取锁时,会建一个节点插入到等待
	队列尾部,需要通过CAS保证原子性插入操作,失败的话,说明有其它线程插入成功,进行重新尝试;当一个
	线程释放锁,它会修改状态并唤醒等待队列中的后继节点,也是CAS操作,因为可能存在多线程同时竞争唤醒
        同一个节点的情况
    

Lock 和 synchronized的区别

第一方面,功能角度,两者都是java中解决线程安全问题的工具

第二方面,特性来看
    a.sync是java的关键字,Lock是JUC包的接口,API,这个接口有很多实现类,其中包括ReentrantLock重入锁
    b.sync可以通过两种方式控制锁粒度
    // 修饰方法
    public synchronized void mehhod(){
   }

	Object lock = new Object();
	// 修饰代码块
    public void method(){
   
        synchronized(lock){
   }
    }
	如上,一种是修饰方法,一种是修饰代码块,另外锁对象是静态对象或类对象,锁是全局锁,整个类;锁的是普通
	实例对象,那么锁的范围就是该实例的生命周期;
	lock锁粒度通过它里面提供的lock()unlock()方法决定,包裹在两方法间的代码能保证线程安全;
	Lock lock = new ReentrantLock();
    public void method(){
   
        lock.lock();  // 竞争锁
        // 代码
        lock.unlock(); // 释放锁
    }
	c. lock比sync灵活度更高,Lock可以决定什么时候加锁,什么时候释放锁,两个方法即可,还提供了非阻塞
        的竞争锁方法tryLock(),返回值真假来告诉当前线程是否已经有其它线程正在使用锁
        sync由于是关键字,无法实现非阻塞竞争锁的方法,锁的释放是被动的,必须同步代码块执行完或者异常
        才会释放
    d. lock提供公平锁和非公平锁机制,sync只有非公平锁实现

第三方面,性能来看,差别不大,实现有一些区别,sync引入锁升级优化,而lock用自旋锁方式实现性能优化

image.png
:::success
备注 : 上表第六行有一个错别字, Lock 是可中断类型的锁;
:::

线程池如何知道一个线程的任务已经执行完成

第一种 : 线程池内部,工作线程执行run方法,,run方法正常结束,任务执行结束;等run方法返回,可以统计任务
    	的完成数量;

第二种 : 如果在线程池外部去获得内部任务的执行状态,
    	方法一 : isTerminated()方法,可判断线程池运行状态,循环判断,一旦状态是Terminated意味任务
                执行完.前提是程序主动调用线程池shutdown()方法,因此实用性灵活性不够;
    	方法二 ; 在线程池中,有一个 submit()方法,它提供了一个 Future 的返回值,我们通
                过 Future.get()方法来获得任务的执行结果,当线程池中的任务没执行完之前,
                future.get()方法会一直阻塞,直到任务执行结束。因此,只要 future.get()
                方法正常返回,也就意味着传入到线程池中的任务已经执行完成了!
    	方法三 : 可以引入一个 CountDownLatch 计数器,它可以通过初始化指定一个计数器
                进行倒计时,其中有两个方法分别是 await()阻塞线程,以及 countDown()
                进行倒计时,一旦倒计时归零,所以被阻塞在 await()方法的线程都会被释放。
综上 : 不管是线程池内部还是外部,要想知道线程是否执行结束,我们必须要获取线程执行结束后的状态,而线程
    本身没有返回值,所以只能通过阻塞-唤醒的方式来实现,future.get 和 CountDownLatch 都是这样原理。

image.png

阻塞队列的有界和无界

1.阻塞队列,是一种特殊队列,是在普通队列基础上提供两个附加功能:
	a. 当队列为空的时候,获取队列中元素的消费者线程会被阻塞,同时唤醒生产者线程。
	b. 当队列满了的时候,向队列中添加元素的生产者线程被阻塞,同时唤醒消费者线程。
2.其中,阻塞队列中能够容纳的元素个数,通常情况下是有界的,比如我们实例化一
	个 ArrayBlockingList,可以在构造方法中传入一个整形的数字,表示这个基于数
	组的阻塞队列中能够容纳的元素个数。这种就是有界队列。
3. 而无界队列,就是没有设置固定大小的队列,不过它并不是
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值