并发编程常见问题(3)

124 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

并发三要素

原子性:不可分割的操作,多个步骤要保证同时成功或同时失败

有序性:程序执行的顺序和代码的顺序保持一致

可见性:一个线程对共享变量的修改,另一个线程能立马看到

volatile关键字

在并发领域中,存在三大特性:原子性、顺序性、可见性,volatile关键字用来修饰对象的属性,在并发环境下可以保证这个属性的可见性,对于加了volatile关键字的属性,在对这个属性进行修改时,会直接将cpu高级缓存中的数据写回到主内存,对这个变量的读取也会直接从主内存中读取,从而保证了可见性,底层是通过操作系统的内存屏障来实现的,由于使用了内存屏障,所有会禁止指令重排,所以同时也就保证了有序性,在很多并发场景下,如果用好volatile关键字可以很好的提高执行效率

线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程?

1、一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务。

阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源

阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一直占用cpu资源

2、在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体的效率

Synchronized是怎么进行所升级的

偏向锁->轻量级锁

线程A请求synchronized对象,比较同步代码块对象存储的线程S ID与A ID是否相同,如果相同,那么执行同步块,无需使用CAS加锁或解锁;如果不同,那么检查synchronized对象中记录的线程S是否存活,如果线程S未存活,那么synchronized对象被重置为无所状态,线程A通过竞争将其设置为偏向锁,此时,synchronized对象头中存储线程A的ID,如果线程S存活,那么查找线程S的针毡信息,如果线程S仍要继续持有该synchronized对象,那么暂停线程S,撤销偏向锁,升级为轻量级锁,如果线程S不在使用synchronized对象,将synchronized对象设为无所状态,重新偏向线程A

轻量级锁->重量级锁

线程A获取轻量级锁时先将synchronized对象头Mark Word复制一份到线程A在栈帧中创建的存储锁记录空间,然后使用CAS将对象头中的内容替换成为线程A存储的锁记录地址。如果线程A复制对象头,线程B也准备获取锁,复制对象头到线程B的锁记录空间,当线程B进行CAS时发现,线程A已经将对象头替换,线程B的CAS失败,线程B尝试使用自旋等待线程A释放锁。如果线程B自旋次数到了上线,线程A扔没有释放锁,线程B仍在自旋等待,此时,线程C又来竞争锁对象,轻量级锁会膨胀为重量级锁。重量级锁未获得所得线程阻塞。防止CPU空运行。

CAS和Synchronize的区别

Synchronized:是一种悲观锁。 会导致未获得锁的线程阻塞,等待持有锁的线程释放锁。

CAS是一种无锁化机制,主要依靠volatile修饰变量,保证变量的可见性,并通过cpu提供的一套机制保证其原子性

线程池的submit和execute方法区别

1、接收的参数不一样

2、submit()有返回值,而execute()没有

3、submit()可以进行Exception处理