并发编程八股

并发编程

保证数据的一致性

事务管理,锁机制,版本控制

线程的创建方式

继承Thread类,重写其run()方法,调用start()方法启动线程

实现Runnable接口,重写run()方法,调用start()方法启动线程

实现Callable接口与FutureTask

使用线程池(Executor框架)

停止一个线程的运行

异常法停止:线程调用interrupt()方法后,在线程的run方法中判断当前对象的interrupted()状态,如果是中断状态则抛出异常,达到中断线程的效果。
在沉睡中停止:先将线程sleep,然后调用interrupt标记中断状态,interrupt会将阻塞状态的线程中断。会抛出中断异常,达到停止线程的效果
stop()暴力停止:线程调用stop()方法会被暴力停止,方法已弃用,该方法会有不好的后果:强制让线程停止有可能使一些请理性的工作得不到完成。
使用return停止线程:调用interrupt标记为中断状态后,在run方法中判断当前线程状态,如果为中断状态则return,能达到停止线程的效果。

Java线程的状态

NEW:尚未启动的线程状态,即线程创建,还未调用start方法
RUNNABLE:就绪状态(调用start,等待调度)+正在运行
BLOCKED:等待监视器锁时,陷入阻塞状态
WAITING:等待状态的线程正在等待另一线程执行特定的操作(如notify)
TIMED_WAITING:具有指定等待时间的等待状态
TERMINATED:线程完成执行,终止状态

notify 和 notifyAll 的区别

唤醒随机一个和全部

Java中有哪些常用的锁

内置锁(synchronized)

ReentrantLock

读写锁(ReadWriteLock)

乐观锁和悲观锁

自旋锁

synchronized 工作原理

synchronized是Java提供的原子性内置锁,使用synchronized之后,会在编译之后在同步的代码块前后加上monitorenter和monitorexit字节码指令,他依赖操作系统底层互斥锁实现

waitSet和entryList+计数器

无锁、偏向锁、轻量级锁和重量级锁

reentrantlock工作原理

底层实现主要依赖于 AbstractQueuedSynchronizer(AQS)这个抽象类。AQS 是一个提供了基本同步机制的框架,其中包括了队列、状态值等

可中断性设置超时时间公平锁和非公平锁多个条件变量可重入性

synchronized和reentrantlock应用场景的区别

synchronized:简单同步需求,代码块同步,内置锁的使用

ReentrantLock:

高级锁功能需求,如公平锁、响应中断;

性能优化:提供了更细粒度的控制;

复杂同步结构

synchronized 支持重入吗?如何实现的?

是可重入的

如果锁状态是0,代表该锁没有被占用,使用CAS操作获取锁,将线程ID替换成自己的线程ID。
如果锁状态不是0,代表有线程在访问该方法。此时,如果线程ID是自己的线程ID,如果是可重入锁,会将status自增1,然后获取到该锁,进而执行相应的方法;如果是非重入锁,就会进入阻塞队列等待。

syncronized锁升级的过程

无锁--没有开启偏向锁

偏向锁--一个线程,还没有一个线程拿到这个锁的话,这个状态叫做匿名偏向,当一个线程拿到偏向锁的时候,下次想要竞争锁只需要拿线程ID跟MarkWord当中存储的线程ID进行比较,如果线程ID相同则直接获取锁(相当于锁偏向于这个线程),不需要进行CAS操作和将线程挂起的操作。

轻量级锁--锁竞争,CAS操作实现,自旋

重量级锁:当有两个以上的线程获取锁,线程会被操作系统调度然后挂起

JVM对Synchornized的优化

锁膨胀:synchronized 从无锁升级到偏向锁,再到轻量级锁,最后到重量级锁的过程

锁消除:检测不到某段代码被共享和竞争的可能性,就会将这段代码所属的同步锁消除掉

锁粗化:将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。

自适应自旋锁:指通过自身循环,尝试获取锁的一种方式

AQS

AQS全称为AbstractQueuedSynchronizer,是Java中的一个抽象类。 AQS是一个用于构建锁、同步器、协作工具类的工具类(框架)

AQS核心思想是,如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是CLH队列的变体实现的,将暂时获取不到锁的线程加入到队列中。

CLH:Craig、Landin and Hagersten队列,是单向链表,AQS中的队列是CLH变体的虚拟双向队列(FIFO),AQS是通过将每条请求共享资源的线程封装成一个节点来实现锁的分配

使用一个Volatile的int类型的成员变量来表示同步状态,通过CAS完成对State值的修改

AQS原理

AQS最核心的就是三大部分:

状态:state;
控制线程抢锁和配合的FIFO队列(双向链表);
期望协作工具类去实现的获取/释放等重要方法(重写)

Threadlocal作用,原理,具体里面存的key value是啥,会有什么问题,如何解决?

ThreadLocal是Java中用于解决线程安全问题的一种机制,它允许创建线程局部变量

Thread类中,有个ThreadLocal.ThreadLocalMap 的成员变量。
ThreadLocalMap内部维护了Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型对象值。

ThreadLocal的作用

线程隔离,降低耦合度,性能优势

ThreadLocal的原理

当创建一个ThreadLocal变量时,实际上就是一个ThreadLocal对象的实例。每个ThreadLocal对象都可以存储任意类型的值,这个值对每个线程来说是独立的。

有内容->get()->获得值

无内容->get()->initialValue()->放入ThreadLocalMap并返回

set()->储存键值对

remove()->移除键值对

存在的问题

ThreadLocal对象本身不会立即被垃圾回收,直到没有其他引用指向它为止

如果不显式调用remove()方法,或者线程结束时未正确清理ThreadLocal变量,可能会导致内存泄漏,因为key是弱引用能被自动回收,但是如果是线程池一类或者什么原因,线程没能销毁,作为强引用的value其实一直存在,导致内存泄漏

悲观锁和乐观锁的区别

Java中想实现一个乐观锁,都有哪些方式

CAS,版本号,时间戳

CAS 有什么缺点

ABA问题,循环时间长开销大,只能保证一个共享变量的原子操作

为什么不能所有的锁都用CAS

大量线程自旋会导致CPU资源浪费

voliatle关键字有什么作用

保证变量对所有线程的可见性(从主存中读取),禁止指令重排序优化(读写屏障)

volatile和sychronized比较

Synchronized: Synchronized是一种排他性的同步机制,代码块或方法

Volatile: Volatile是一种轻量级的同步机制,从内存中读取变量

公平锁和非公平锁

公平锁的优点在于各个线程公平平等,每个线程等待一段时间后,都有执行的机会,而它的缺点就在于整体执行速度更慢,吞吐量更小

非公平锁的优势就在于整体执行速度更快,吞吐量更大,但同时也可能产生线程饥饿问题

Synchronized是公平锁吗

Synchronized不属于公平锁,ReentrantLock是公平锁

如果是公平锁,那么一旦已经有线程在排队了,当前线程就不再尝试获取锁;对于非公平锁而言,无论是否已经有线程在排队,都会尝试获取一下锁,获取不到的话,再去排队

线程池的工作原理

核心线程->等待队列->最大线程->丢弃策略

线程池的参数

核心线程数量,最大线程数量,额外空闲线程存活时间,时间的单位,工作队列,拒绝策略,线程工厂(可以用来给线程取名字等等)

拒接策略

线程池调用者的线程去处理;抛出拒绝异常;不处理并拒绝;丢弃最老的任务;自定义

线程池种类

ScheduledThreadPool:可以设置定期的执行任务

FixedThreadPool:它的核心线程数和最大线程数是一样的

CachedThreadPool:可以称作可缓存线程池,它的特点在于线程数是几乎可以无限增加的

SingleThreadExecutor:它会使用唯一的线程去执行任务

线程池一般是怎么用的

应该手动 new ThreadPoolExecutor 来创建线程池

shutdown (),shutdownNow()

shutdown使用了以后会置状态为SHUTDOWN,正在执行的任务会继续执行下去,没有被执行的则中断。此时,则不能再往线程池中添加任何任务

shutdownNow 为STOP,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,当然,它会返回那些未执行的任务

提交给线程池中的任务可以被撤回吗?

提交会得到一个Future对象。这个Future对象提供了几种方法来管理任务的执行,包括取消任务

posted @ 2024-11-20 17:28  nowiam  阅读(1)  评论(0编辑  收藏  举报