并发视频学习笔记(二)

目录

一、synchronized

1.线程安全问题的主要原因及解决办法

2.互斥锁的特性

3.获取的锁的分类:

二 、synchronized底层实现原理

三、锁优化

1.自旋锁

2.自适应自旋锁

3.锁消除

4.锁粗化

5.synchronized的四种状态

6偏向锁

6.轻量级锁

7.锁的内存语义

8.偏向锁、轻量级锁、重量级锁的汇总

四、synchronized和ReentrantLock(重入锁)

1.ReentrantLock

2.区别

五、JMM和happens-before

1.Java内存模型JMM

2.Happens-before的八大原则

六、volatile

1.概念

2.volatile的可见性

3.为什么volatile可以立即可见?

4.volatile如何禁止重排优化?

5.线程安全的单例写法

6.volatile和synchronized的区别

七、CAS(Compare and Swap)

1.概念

2.思想

3.使用

4.缺点

八、线程池

1.使用Executors的五种方法:

2.Fork/Join框架

3.为什么要使用线程池

4.Executor框架


一、synchronized

1.线程安全问题的主要原因及解决办法

2.互斥锁的特性

(互斥性和可见性)

-》synchronized 锁的不是代码,锁的都是对象

3.获取的锁的分类:

(1)获取对象锁

(2)获取类锁

(3)对象锁和类锁的总结


二 、synchronized底层实现原理

(1)实现基础

-》Java对象头

-》Monitor

(2)对象在内存中的布局

-》对象头

-》实例数据

-》对齐填充

注:实例数据和对齐填充不做展开

(3)对象头的结构

synchronized的锁对象时存储在对象头里的,主要由以下两部分组成

(4)Mark Word:被设计成一个非固定的数据结构

会根据对象本身的状态,复用自己的存储空间

重量级锁(synchonized):10,指针指向monitor的起始位置

(5)Monitor:

(源码层面:是由ObjectMonitor实现的,位于hotspot虚拟机源码,在ObjectMonitor.hpp中,是通过c++实现的)

每个Java对象天生自带了一把看不见的锁

可以将其理解为一个同步工具,也可以理解为一种同步机制,通常他被描述为一个对象

 

当monitor被某个线程持有后,他便处于锁定状态

ObjectMonitor中有两个队列,WaitSet和EntryList,等待池和锁池,他们是用来保存ObjectMonitor的对象列表

每个对象锁的线程都会被封装成ObjectMonitor来保存到里面,其中有个字段owner,他是指向持有ObjectMonitor对象的线程。

当多个线程同时访问同一段代码的时候,首先会进入到EntryList集合里面,当线程获取到对象的Monitor后,就进入到Object区

域,并把Monitor中的Owner变量设置为当前线程。

同时Monitor中的计数器Count就会+1,若线程调用方法,将释放当前持有的Monitor,owner就将恢复成null,count也将-1;

同时该线程,即ObjectWaiter实例,就会被记录到WaitSet中等待被唤醒,若当前线程执行完毕,他也将释放Monitor锁,并复位

对应变量的值,以便其他线程进入获取Monitor锁。

 

Monitor对象存在于每个对象的对象头中,synchronized便是通过这种方式去获取锁的,这也是为什么JAVA中任意对象可以作为锁的原因。

(6)重入

重入会成功,synchronized是可重入的

(7)通常不使用synchronized的原因

自适应自旋

锁消除

锁粗化

轻量级锁

偏向锁

(8)Java6之后,它的性能得到了很大的提升


三、锁优化

 

1.自旋锁

2.自适应自旋锁

由于每次线程需要等待的时间是不固定的,如何设计自旋

3.锁消除

JIT编译器,英文写作Just-In-Time Compiler,中文意思是即时编译器

举例:

4.锁粗化

例如以下这种情况

如果出现一连串操作都对同一个操作反复加锁和解锁,会导致不必要的性能消耗

JVM会把加锁粗化到整个循环外面

5.synchronized的四种状态

6偏向锁

在大多数情况下,锁不仅不存在竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁的代价,引入偏向锁

也就是说,当一个线程访问一个同步块,并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块的时候, 不需要进行CAS操作,从而提高程序性能。

注:偏向锁不适用于锁竞争比较激烈的多线程场合

6.轻量级锁

7.锁的内存语义

8.偏向锁、轻量级锁、重量级锁的汇总


四、synchronized和ReentrantLock(重入锁)

1.ReentrantLock

AQS:AbstractQueuedSynchronizer队列同步器,是JAVA并发用来构建同步锁,或其他同步组件的基础框架,是JUC packet的核心,一般使用AQS的核心是继承

利用AQS去实现一个同步操作,至少要实现两个基本类型的方法,分别是acquire方法,他是用来获取资源的独占权,还有release操作,主要是用来释放对某个资源的独占。

2.区别

(1)公平性

打印结果,不断的争抢锁

如果设置为非公平锁:

会一直打印

 

(2)ReentrantLock将锁对象化

(3)总结


五、JMM和happens-before

1.Java内存模型JMM

JAVA内存模型规定所有变量都存储在主内存中,主内存是共享数据区,所有线程都可以访问。

但是对变量的操作,即读取、赋值等必须在工作内存中执行。

首先将变量从主内存中拷贝到自己的工作内存空间中,然后对变量进行操作,操作完成后再将变量写回主内存

(1)JMM中的主内存

(2)JMM的工作内存

(3)JMM与Java内存区域划分的关系

-》他们是不同的概念层次

(4)主内存与工作内存的数据存储类型以及操作方式归纳

(5)JMM如何解决可见性问题

简单理解为:

把数据从内存加载到缓存寄存器,然后运算结束,写回主内存。

(5)指令重排序

例如以下操作不能指令重排

2.Happens-before的八大原则

(1)八大原则

(2)happens-before的概念


六、volatile

1.概念

2.volatile的可见性

3.为什么volatile可以立即可见?

4.volatile如何禁止重排优化?

5.线程安全的单例写法

(1)不安全:

(2)修改版

6.volatile和synchronized的区别


七、CAS(Compare and Swap)

1.概念

2.思想

执行CAS操作的时候,将内存位置的值与预期原值进行比较,如果相匹配,处理器会将该位置的值更新为新值。

否则处理器不做任何操作,这里的内存位置的值V,即主内存的值。

举例:

当一个线程修改变量的值,完成这个操作,先取出共享变量的值,赋给A,然后基于A的基础进行计算,得到新值B。

执行完毕之后需要更新共享变量的值时,就可以调用CAS方法去更新变量的值

查看字节码:

add方法被拆分成

getfield 加载主内存中的数据到工作内存中

iadd 进行+1的操作

putfield 将操作后的值写回主内存

 

通过volatile可以保证线程之间的可见性,同时也不允许JVM对它们进行重排序;

但是并不能保证这三个指令的执行,在多线程的情况下无法做到线程安全。

 

如何解决?

1.悲观锁

2.AtomicInteger保证原子性

 

 

3.使用

一般情况下使用JAVA提供的包即可

 

4.缺点

ABA问题:一个变量A被改变为B后又被改变为A

通过控制变量值的版本来保证CAS的正确性



八、线程池

在web开发中,服务器需要接收并处理请求,服务器会为一个请求分配一个。

如果并发的请求非常多,但是每个请求的时间很短,就会使用线程池。(因为需要频繁请求和销毁)

1.使用Executors的五种方法:

1.可以指定线程数量

每当一个任务去创建一个工作线程,如果工作线程达到线程池的初始数量,则将提交的任务存在池队列中。

如果有工作线程退出,则会有新的工作线程被创建,以补足nThreads的数目。

2.处理大量短时间工作任务的线程池

3.创建一个单线程化的Executor

最大的特点是可以按顺序的去执行线程,并且在任务的给定时间,不会有多个线程是活动的

4.定时或者周期性的工作调度,前者是单一线程,后者是由多个线程来组成的

5.JDK8才引入的

内部会构建ForkJoinPool,利用working-stealing算法,并行地处理任务,不保证处理顺序。

2.Fork/Join框架

为每个任务创建一个任务队列,每个任务分配一个线程

已经完成自己任务并且空闲的线程能够从其他线程窃取任务,利用的是双端队列。

被窃取线程从头部执行任务,窃取任务的线程则是从尾部

3.为什么要使用线程池

 

4.Executor框架

该框架是一个根据一组执行策略调用,调度、执行和控制的异步任务的框架,目的是提供一种将任务提交与任务如何运行分离开来的机制

5.JUC的三个Executor接口

 

JAVA标准库提供了上述三种接口的几种基础实现,这些线程池的特点在于其高度的可调节性,以尽量满足复杂多变的应用场景。

Executors则从简化使用的角度,提供了各种方便的静态工厂方法。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMUHERO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值