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,如果还是冲突依次计算直到超过数组下标,这个时候又重头开始,相当于一个环形数组

目录
相关文章
|
4天前
|
Java 调度
Java线程的六种状态
Java线程有六种状态: 初始(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、超时等待(TIMED_WAITING)、终止(TERMINATED)。
13 1
|
4天前
|
存储 安全 Java
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
Java面试题:请解释Java内存模型(JMM)是什么,它如何保证线程安全?
35 13
|
1天前
|
安全 Java 开发者
Java并发编程中的线程安全性与性能优化
在Java编程中,处理并发问题是至关重要的。本文探讨了Java中线程安全性的概念及其在性能优化中的重要性。通过深入分析多线程环境下的共享资源访问问题,结合常见的并发控制手段和性能优化技巧,帮助开发者更好地理解和应对Java程序中的并发挑战。 【7月更文挑战第14天】
|
1天前
|
监控 Java API
Java并发编程之线程池深度解析
【7月更文挑战第14天】在Java并发编程领域,线程池是提升性能、管理资源的关键工具。本文将深入探讨线程池的核心概念、内部工作原理以及如何有效使用线程池来处理并发任务,旨在为读者提供一套完整的线程池使用和优化策略。
|
4天前
|
缓存 安全 Java
Java中线程池如何管理?
【7月更文挑战第11天】Java中线程池如何管理?
9 2
|
4天前
|
安全 算法 Java
Java中线程安全怎么做?
【7月更文挑战第11天】Java中线程安全怎么做?
10 2
|
3天前
|
存储 安全 算法
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第72天】 在现代软件开发中,尤其是Java应用开发领域,并发编程是一个无法回避的重要话题。随着多核处理器的普及,合理利用并发机制对于提高软件性能、响应速度和资源利用率具有重要意义。本文旨在探讨Java并发编程的核心概念、线程安全的策略以及性能优化技巧,帮助开发者构建高效且可靠的并发应用。通过实例分析和理论阐述,我们将揭示在高并发环境下如何平衡线程安全与系统性能之间的关系,并提出一系列最佳实践方法。
|
4天前
|
安全 算法 Java
Java面试题:如何使用并发集合,例如ConcurrentHashMap?
Java面试题:如何使用并发集合,例如ConcurrentHashMap?
14 1
|
4天前
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
18 1
|
3天前
|
Java 调度
java中线程的6种状态
java中线程的6种状态