java多线程以及并发相关知识

简介: 1、进程是操作系统进行资源分配的最小单元2、线程是执行操作的最小单元

目录

一、前言

大家好,我是苍何。最近思考了一个问题,为什么会出现公司面试造火箭,工作扭螺丝的现象,包括各种八股文的连环大绝杀问到你不会为主,其实这是考察你的知识面以及掌握的深度,而为什么需要这样呢?归其原因,无非是通过筛选找到那些会思考的人,他们需要的并不是CRUD的工具人,而是会思考能创新的工程师。

当你深刻理解到这点,我想不用刻意去学习,在工作中,肯定会吾日三省吾身。

于是乎,这个重新开始学习编程系列文章出来了。

愿与君共勉!

二、线程和进程的区别

1、进程是操作系统进行资源分配的最小单元
2、线程是执行操作的最小单元

三、重量级锁和轻量级锁

重量级就是要通过操作系统来进行锁的操作,轻量级直接在JVM中就可以进行

四、如何开启线程

1、继承Thread类,重写run方法
2、实现Runnable接口,实现run方法
3、实现Callable方法,通过FutureTask创建一个线程,获得线程执行的返回值
4、线程池创建线程

五、如何保证线程安全

1、JVM提供的关键字Synchronized
2、JDK提供的各种锁

六、volatile

1、线程变量可见性(一个线程写,多个线程读)
2、防止指令重排(DCL单利需要加volatile)
3、不能保证线程安全,不能保证原子性

七、CAS

一、什么是CAS
比较并交换(Swap)-compareAndSet
二、unsafe类
UnSafe类是CAS的核心类,由于Java无法直接访问底层系统,所以要通过本地的native方法进行访问,UnSafe类就相当于一个后门,基于该类可以直接操作特定内存中的数据,其内部就像C的指针一样操作内存。观察UnSafe类的源码,可以看到UnSafe类都是native方法,也就是说Unsafe类都是直接调用操作系统底层资源执行任务。

三、CAS底层原理
1、CAS是一种CPU的原子指令,不会造成所谓的数据不一致情况
2、在Java中 CAS 底层使用的就是自旋锁 + UnSafe类。
四、CAS实现原⼦操作的三⼤问题?解决⽅案
1、ABA问题
个变量是A,变成了B,又变成了A,那么使用CAS进行检查时是发现没有变化,但是的确是变化了
解决方案:加一个版本号去做标识,AtomicStampedReference类compareAndSet方法(作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设定为给定的更新值)
2、循环时间长开销大
使用pause指令
延迟流水线执行指令de-pipline,使CPU不会消耗过多的执行资源,延迟的时间取决与具体实现的版本,在一些CPU上延迟时间是0
避免在退出循环的时候因内存顺序冲突引起CPU流水线清空,从而提高CPU执行效率。
3、只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环cas(自旋锁)的方式保证原子性,但是对多个共享变量操作时,循环CAS就无法保证操作原子性,这时后可以取用锁,,也可以取巧把多个共享变量合并成一个共享变量操作。
从java1.5时,JDK提供了AtomicReference类保证引用对象之间的原子性。

四、Atomic类如何保证原⼦性(CAS操作)
通过Unsafe类,实现的原子性。而Unsafe类是由native方法实现的,unsafe.getAndAddInt(this, valueOffset, 1),直接调用操作内存

八、java锁机制是怎样的

java锁就是在对象的markword中记录一个锁状态

九、偏向锁、轻量级锁、重量级锁什么区别

1、对应不同的锁状态
2、偏向锁就是类似一个门,请大家有序访问,并说获得该锁的线程是
3、轻量级锁也叫自旋锁,就是不断循坏看是否能获得锁
4、重量级锁:由操作系统进行统一管理

十、锁升级机制

1、new对象默认是不开偏向锁,等过了4s后再打开偏向锁
2、new一个对象先开一个偏向锁,有资源轻度竞争加轻量级锁,竞争比较激烈就用重量级锁

十一、AQS

1、是一个java线程同步的框架,是JDK中很多锁工具的核心实现框架
2、在AQS中维护了一个信号量state,和一个线程组成的双向链表队列,其中这个队列就是用来给线程排队的,而state就像是一个红绿灯,用来控制线程排队或放行,在不同场景下有不同的意义

十二、可重入锁

ReetrantLock
可多次锁住和释放锁,
AQS如何实现可重入锁
AQS中的state用来表示加锁的次数,0表示无锁,每加一次锁,state就加1,释放锁就减1

十三、三个并发工具

1、CountDownLatch
保证三个线程同时执行,可以让我们模拟高并发的场景
2、CylicBarrier
并发情况下三个线程依次执行
3、Semaphore
三个线程有序交错执行

十四、fork/join框架

拆分合并的过程

十五、synchronized

一、对象头
1、存储对象自身的运行时数据
哈希码
GC分代年龄
锁状态标识
线程持有的锁
偏向线程id
偏向时间戳
2、类型指针
3、若为对象数组,还有记录数组长度的数据

Java对象头一般占有2个机器码(在32位虚拟机中,1个机器码等于4字节,也就是32bit,在64位虚拟机中,1个机器码是8个字节,也就是64bit),但是 如果对象是数组类型,则需要3个机器码,因为JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。

二、synchronized和Lock的区别(
1、synchronized是java关键字,是在JVM层面上,lock是一个接口,是JDK提供的
2、synchronized自动释放,lock需要手动释放
3、synchronized其他线程获取不到锁智能等待,lock提供了trylock方法可以不阻塞等待,尝试获得锁

4、synchronized可重入、不可中断,非公平,lock可重入、可判断、可公平
5、synchronized原始采用悲观锁,lock采用乐观锁的方式

十六、线程池

一、ThreadPoolExecutor的七个参数
private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue(10));

1、corePoolSize:
核心线程数量,
2、maximumPoolSize:
最大线程数
3、keepAliveTime:
存活时间
4、unit:
超时时间的单位
5、BlockingQueue  :
任务队列
6、ThreadFactory  :
线程工厂
7、RejectStrategy  :
拒绝策略

二、ThreadPoolExecutor的⼯作流程

1、在创建了线程池后,开始等待请求
2、当调用execute()方法添加一个请求任务时,线程池会做出如下判断
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
3、如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列
4、如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务
5、如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略handler来执行
6、当一个线程完成任务时,它会从队列中取下一个任务来执行
7、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:
8、如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。

三、线程池的状态⽣命周期,他们是怎么流转的
线程池的状态一共有五种,分别是RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED;

1、RUNNING
运行态,表示可接受新任务,且可执行队列中的任务
2、SHUTDOWN
关机
表示不接受新任务,但可执行队列中的任务;
3、STOP
停止

表示不接受新任务,且不再执行队列中的任务,且中断正在执行的任务;

4、TIDYING
清理
所有任务已经中止,且工作线程数量为0,最后变迁到这个状态的线程将要执行terminated()钩子方法,只会有一个线程执行这个方法;
5、TERMINATED
中止状态,已经执行完terminated()钩子方法

四、你们项⽬中怎么⽤的线程池
查询客户动态的时候,分发三个线程去分别去获取客户的名片动态、访问项目动态以及客户留点动态

十七、ThreadLocal

一、是什么?
ThreadLocal是解决多线程下共享变量并发问题,即同一个变量在不同线程下赋予不同值。

二、ThreadLocal和synchronized区别?
synchronized是时间换空间让多个线程排队访问,ThreadLocal是空间换时间为每个线程提供了一份变量的副本,从而实现线程隔离。

三、ThreadLocal的内部结构
1、每一个thread维护一个threadlocalmap,threadlocalmap是由threadlocal维护的,map里面存的key是threadlocal对象本身,value是变量副本

2、set方法
先获取当前线程,获取线程的threadlocalmap,(获取不到就创建一个map),将当前ThreadLocal为key,value为值set进去
3、get方法
获取线程的的threadlocalmap,获取到ma且根据key(当前threadlocal)获取到值就返回,如果map是空或者获取不到值就返回一个默认的

3、initialValue方法
初始值,可以被继承,设置默认初始值

四、threadlocalmap基本结构
1、是threadlocal静态内部类
2、key是threadlocal对象是弱引用,目的是将threadlocal对象的生命周期和线程的生命周期解绑

五、弱引用和内存泄漏

1、内存泄漏和内存溢出
内存溢出:
程序申请内存,没有足够的空间供其使用,out of memory
内存泄漏:
无用对象无法被GC回收,始终占用内存,造成空间浪费,最终会导致内存溢出,
2、弱引用
垃圾回收期会回收
3、threadlocalmap使用了弱引用也会出现弱引用

4、threadlocal出现内存泄漏的真实原因

A、没有手动删除Entry对象,使用完threadlocal调用其remove方法就可以删除对应的Entry,避免内存泄漏
B、threadlocal的使用,thread也随之结束

根本原因:threadlocalmap和thread生命周期是一样的。

六、threadlocalMap使用线性探测法来解决冲突的

如果hash冲突,数组下标加1,如果还是冲突依次计算直到超过数组下标,这个时候又重头开始,相当于一个环形数组

目录
相关文章
|
3月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
100 4
|
3月前
|
缓存 安全 Java
如何理解Java中的并发?
Java并发指多任务交替执行,提升资源利用率与响应速度。通过线程实现,涉及线程安全、可见性、原子性等问题,需用synchronized、volatile、线程池及并发工具类解决,是高并发系统开发的关键基础。(238字)
256 5
|
3月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
213 2
|
3月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
202 1
|
3月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
229 1
|
4月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
182 0
|
4月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
300 16
|
5月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
5月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践