java基础拾遗

1.JVM的作用
jvm是运行java字节码的虚拟机,目的是将字节码根据不同的平台序列化为每个平台对应的机器码。
2.jvm序列化的知识
jvm序列化的实体是一个对象,结果也是个对象,在实际使用对象序列化的时候,有两种场景
第一个场景是将对象序列化到 持久化形式的存储当中(本地硬盘),我们需要的时候,可以采用反序列化的形式将保存的文件生成对象。
第二种场景是在网络传输过程中,对象在不同主机之间的传播,序列化会将对象转成码流由接收方进行解析

Volatile关键字

volatile修饰的变量不允许线程内部缓存和重排序。他是直接修改缓存的,所以对其他线程是可见的。
Volatile 是一种***稍弱的同步机制***(不会进行加锁操作,因此也不会使得线程阻塞),当变量声明为Volatile类型后,这个变量的更新操作会通知到其他线程。(直接修改主内存变量,跳过CPU cache 这一步)

Volatile 无法保证线程原子性,需要和CAS相结合。

JAVA包装类

在这里插入图片描述

为什么需要包装类

因为java是面向对象编程,但是java中的基本数据类型却是不面向对象的,为了解决这个问题,在设计类的时候为每一个基本类型设计一个对应得类进行代表。

包装类得目的:

1.提供一种机制,将基本值封装在对象里面。
2.为基本值提供分类功能,这些功能用于各种转换,例如 基本值和String对象转换,进制转换

装箱和拆箱

装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的。(xxx代表对应的基本数据类型)

hashmap

HashMap 源码解析

hashmap底层是数组+链表的形式存放数据的。
hashmap核心成员变量。
在这里插入图片描述
1.初始化桶大小,因为底层是数组,所以这是数组默认的大小。
2. 桶最大值。
3。默认的负载因子(0.75)
4. table 真正存放数据的数组。
5. Map 存放数量的大小。
6. 桶大小,可在初始化时显式指定。
7. 负载因子,可在初始化时显式指定。
默认的容量为16,负载因子为0.75,则在使用的时候,当hashmap容量到12的时候,进行扩容(扩容需要涉及到rehash,复制数据等操作,所以非常消耗性能,建议预估HashMap的容量大小,减少扩容带来的性能损耗

put:根据key计算出hashcode,根据hashcode定位数据要存放的桶(Entry),如果在hashcode地址上已经有key值,则 利用 equal方法,相等则覆盖,不相等则在链表末尾利用addEntry方法加一个桶。
get方法:根据key计算出hashcode,定位到具体的桶中,判断此位置是不是链表,不是链表则根据key,hashcode的相等来取值,是链表的话,则便利链表,直至key和hashcode都相等的时候返回所在值,取不到值则返回NULL。

hashMap的底层结构

在这里插入图片描述

JDK BASE8

JDK 7的时候,相同hashcode值后面的链表桶(K,V)会出现越来越长的问题,导致查询效率低,JDK8优化了这个查询效率。
在这里插入图片描述
源码成员重要区别:
1.Theeify_threshold 用来判断是否需要将链表转化为红黑树的阈值。
2.HashEntry修改为Node,(核心存放数据相同 kv ,hashcode,next数据)
put的区别是:put进数据的时候,判断链表是否达到转化为红黑树的阈值,如果达到了,则按照红黑树的方式写入数据,否,则按照链表的形式写入数据。

HashMap缺点

JDK 7,JDK8都没有对HashMap进行同步操作。
HashMap在进行扩容操作的时候(Resize),容易在一个桶中造成死锁,争夺链表的下标。
因此JDK推出了专项专用的ConcurrentHashMap,用于解决并发问题。
JDK 7 分段锁 Segment+HashEntry,value属性使用Volatile修饰,保证了内存可见性。
缺点:查询效率太低。
JDK 8 使用CAS+Synchronized来保证并发安全性。使得val,next以及value都加了volatile关键字,保证可见性。

线程池
使用线程池的目的

1.线程是稀缺资源,不能被频繁的创建
2.解耦,线程的创建和使用完全分开,方便维护
3.将线程放入一个池子里,可以给其他任务复用。

线程池原理

线程池 采用的池化技术,是将资源全部放入一个池子中,每次使用从里面获取,使用完毕后放回到池子中。
在JDK1.5之后,常见的创建线程池的方法有如下几种:
Executors.newCachedThreadPool(); 无限线程池
Executors.newFixedThreadPool(nThread) 创建固定最大并发数的线程池
Executors.newScheduledThreadPool 定时线程池
Executors.newSingleThreadExecutor() 创建单个线程的线程池

底层源码都是利用ThreadPoolExecutor类实现的
核心参数有
corepoolsize 线程池基本大小
maximumpoolSize 线程池最大线程大小
keepAliveTime 线程空闲后的存活时间
workQueue 用来存放任务的阻塞队列
handler 线程队列的饱和策略

线程的运行状态

在这里插入图片描述
Running 是运行状态,可以接受任务执行队列里面的任务
Shutdown 调用了shutdown()方法,不再接受新任务,等待队列里面的任务执行完毕。
stop 调用了shutdownNow()方法,不再接受信任我,同时抛弃阻塞队列里面的任务,并中断所有正在执行的任务。
Tidying 所有任务都调用完毕,在调用shutdown/shutdownnow方法的时候都尝试更新为这个状态

Terminated 终止状态,当执行terminated()后更新这个状态。

如何配置线程

1.IO密集型任务,可以尽可能的多线程配置
2.CPU密集型任务,应当分配较少的线程,最好线程个数与CPU个数保持一致。

优雅的关闭线程池

shutdown() shutdownNOW()

并发编程三要素

原子性,可见性,有序性

实现可见性的方法有哪些

synchronized或者lock或者volatile 关键字,

多线程的好处
1.发挥多核CPU的优势
2.防止阻塞(一条不行,另一条不同协议的线程上)

创建多线程的几种方式

1)继承Thread类创建线程类
2)通过Runnable接口创建线程类
3)通过Callable和Future创建线程
4)通过线程池创建

Runnable与Callable的区别
Callable重写方法是 call() ,可抛出异常,并可拿到一个Future对象
Future对象表示异步运算的结果,提供了检查计算是否完成的方法可以取消任务的执行,还可以获得执行结果。
Runnable 重写方法是 run(),不可抛出异常,

什么是CAS 乐观锁

cas是compare and swap的缩写,就是常说的比较交换,内部有三个操作数,
1.内存地址 V 2.旧预期值 A 3.即将要更新的目标值 B
cas指令执行的时候,当且仅当 V=A 的时候,将V改为B,否则什么都不做,是一个原子操作。

CAS造成ABA的问题,没操作一次 version+1

悲观锁和乐观锁的区别

乐观锁对发生的线程安全问题持轻松态度,不需要持有锁,通过CAS原子替换内存里面的值,如果失败表示冲突,失败了就会有相应的重试机制
悲观锁对线程发生的线程安全问题持有悲观状态,因此每次对资源进行操作的时候,都会加锁。

sleep() or wait() 的区别

相同:都是主动放弃CPU一段时间的
不同点: sleep不会释放当前线程的锁,wait会释放当前线程的锁。

死锁是怎样发生的

多个线程涉及多个锁,锁存在着交叉,可能导致了一个锁依赖的闭环。

如何破坏死锁

1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

破坏其中一个条件即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值