java总结详情

主要部分:基础知识,jvm,多线程,spring,spring cloud alibba 

java8种基本类型

整型:byte、short、int、long 2、字符型:char 3、浮点型:float、double 4、布尔型:boolean

String、StringBuffer与StringBuilder的区别

1、String类的内容是不可改变的。能改变的只是其内存指向。

2、String对象不可修改指的是对象本身不可修改,而不是引用不可修改。

3、StringBuffer类的对象内容是可以修改的。

4、StringBuffer在处理字符串的时候,不会生成新的对象。从内存这个角度来说,StringBuffer要比String更好。

6、StringBuffer是线程安全的,速度慢。 StringBuilder是线程不安全的,不能同步访问,执行速度快。

java集合,arrayList和LinkList,hashMap之类

ArrayList是基于数组实现的,LinkedList是基于双链表实现的,因此LinkedList可以作为双向队列 ,栈。

因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。Array获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大的,因为这需要移动数组中插入位置之后的的所有元素。

ArrayList数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

LinkList是双向链表,现在比较少用,因为查询慢,而且新增删除也不比arraylist快很多基本已经被淘汰了。

hashmap采用的有两种实现方法,一种是开放寻址法,就是在哈希冲突的时候再寻找下一个地址。java使用的是另外一种更常见的拉链法。

JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表节点个数超过了8个,且数组大于等于64,将链表转化为红黑树,以减少搜索时间

HashMap的默认初始容量为16,加载因子0.75,也就意味着当HashMap中存储的Entry数量达到16*0.75时会进行一次扩容操作;当我们通过HashMap的有参构造自定义一个初始容量时,给定的值必须是2的幂次方值;

注解

注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。

1,使用@interface关键词来定义注解

2、@Documented:指定被标注的注解会包含在javadoc中。  

3、@Retention: 指定注解的生命周期(源码、class文件、运行时)

4、@Target:指定注解使用的目标范围(类、方法、字段等)

5、@Inherited:指定子类可以继承父类的注解

6、@Native:指定字段是一个常量,其值引用native code。

7、@Repeatable:注解上可以使用重复注解,即可以在一个地方可以重复使用同一个注解

spi机制

SPI机制是Java的一种服务发现机制,为了方便应用扩展。那什么是服务发现机制?简单来说,就是你定义了一个接口,但是不提供实现,接口实现由其他系统应用实现。你只需要提供一种可以找到其他系统提供的接口实现类的能力或者说机制。这就是SPI机制

要使用JDK中的SPI机制有几个前提条件

服务提供方必须实现目标接口

服务提供方必须在自身ClassPath:META-INF/services/路径下建立文件,文件名为目标接口全限定名。文件内容为实现目标接口的具体实现类全限定名

缺点 不能单独获取某个类,只能获取所有,所以dubbo单独实现了自己的dubbo SPI的机制

多线程和线程池 (java内存模型,并发集合,线程池,阻塞队列,CAS与原子操作,无锁并发框架Disruptor )

创建线程和启动

继承Thread类创建线程类 实现Runnable接口创建线程类 通过Callable和Future创建线程(使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。)

状态

线程的生命周期 1、新建状态 2、就绪状态 3、运行状态 4、阻塞状态 5、死亡状态

多线程内存模型

Java内存屏障 

是一种硬件或软件机制,用于确保多线程程序中的内存可见性和有序性。

内存屏障是插入两个CPU命令之间的命令,禁止处理器命令的重新排序(如屏障),以确保有序性。此外,为了达到屏障的效果,在处理器写入、读取值之前,将主机的值写入缓存,清空无效的队列,保障可见性。

Java内存屏障可以通过synchronized、volatile、Lock等关键字来实现。在Java8中,如AtomicInteger、AtomicLong等,可以更方便地实现内存屏障。

JMM三个特性(线程安全性体现方面)

线程的安全性问题体现在:原子性、可见性、有序性。

体现方面说明导致原因解决方法
原子性一个或者多个操作在CPU执行的过程中不被中断的特性线程切换synchronized LOCK JDK Atomic开头的原子类(非阻塞CAS算法)
可见性一个线程对共享变量的修改,另外一个线程能够立刻看到缓存synchronized LOCK volatile
有序性程序执行的顺序按照代码的先后顺序执行编译优化Happens-Before规则

如果A happens-before B,且B happens-before C,那么A happens-before C

线程管理

1、线程睡眠——sleep

2、线程让步——yield

3、线程合并——join

4、设置线程的优先级

5、后台(守护)线程(举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等)


6、正确结束线程

线程同步

synchronized修饰方法或者代码块

volatile修饰单变量单操作实现可见性

使用重入锁(Lock)实现线程同步

线程通信

1、借助于Object类的wait()、notify()和notifyAll()实现通信

2、使用Condition控制线程通信

3、使用阻塞队列(BlockingQueue)控制线程通信

线程池

合理利用线程池能够带来三个好处。

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

Executor线程池框架的最大优点是把任务的提交和执行解耦。客户端将要执行的任务封装成Task,然后提交即可。而Task如何执行客户端则是透明的。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果。

ExecutorService关闭方法

ExecutorService.shutdown()方法 (优雅关机)

ExecutorService.shutdownNow()方法

Executors四种创建线程池方式比较

newFixedThreadPool 定长

newSingleThreadExecutor 唯一

newCachedThreadPool 最大线程数为无界Integer.MAX_VALUE

newScheduledThreadPool 创建支持定时、周期任务的线程池

我们可以从这两点去考虑线程池的配置:

(1)cpu密集型任务,需要线程长时间进行复杂的运算,这种类型的任务需要少创建线程,过多的线程将会频繁引起上下文切换,降低任务处理速度。

(2)io密集型任务,由于线程并不是一直在运行,可能大部分时间在等待io读取/写入数据,增加线程数量可以提高并发度,尽可能多处理任务。

ThreadPoolExecutor入参详解

corePoolSize(核心线程大小)

runnableTaskQueue(任务队列)

maximumPoolSize(最大线程数)

RejectedExecutionHandler(拒绝策略)

keepAliveTime(线程活动保持时间)

TimeUnit(线程活动保持时间的单位)

ThreadFactory(线程工厂,设置名称,是否守护进程)

拒绝策略:
当核心满了,队列满了,最大核心满了,就会启用拒绝策略
AbortPolicy(使用该策略时,如果线程池队列满了丢掉这个任务并且抛出)

DiscardPolicy(如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。)

DiscardOldestPolicy(如果队列满了,会将最早进入队列的任务删掉腾出空间,再尝试加入队列。)

CallerRunsPolicy(如果添加到线程池失败,那么主线程会自己去执行该任务,不会等待线程池中的线程去执行)

自定义(实现RejectedExecutionHandler接口,一般是爆出消息通知用户,然后记录参数)

死锁

(1)死锁的四个必要条件

互斥条件:资源不能被共享,只能被同一个进程使用

请求与保持条件:已经得到资源的进程可以申请新的资源

非剥夺条件:已经分配的资源不能从相应的进程中被强制剥夺

循环等待条件:系统中若干进程组成环路,该环路中每个进程都在等待相邻进程占用的资源

处理死锁的方法

忽略该问题,也即鸵鸟算法。当发生了什么问题时,不管他,直接跳过,无视它;

检测死锁并恢复;

资源进行动态分配;

破除上面的四种死锁条件之一。

线程相关类

ThreadLocal

ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 总结:若多个线程之间需要共享资源,以达到线程间的通信时,就使用同步机制;若仅仅需要隔离多线程之间的关系资源,则可以使用ThreadLocal。

ThreadLocal如何避免内存泄漏 因为Entry对象中的Key的弱引用,但是value还会存在,就会存在map中key为null的value,所以使用这个的时候我们要注意显式使用remove方法去释放这个null值

JUC

阻塞队列

BlockingQueue提供了线程安全的队列访问方式:

当阻塞队列插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;

从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空;

应用场景

线程池 生产者-消费者模型   消息队列 缓存系统 并发任务处理

实现方法:

BlockingQueue 接口的实现类都被放在了 juc 包中,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理却是类似的。

队列    描述
ArrayBlockingQueue    基于数组结构实现的一个有界阻塞队列(利用了Lock锁的Condition通知机制进行阻塞控制,存取是同一把锁,低吞吐量)
LinkedBlockingQueue    基于链表结构实现的一个无界阻塞队列,指定容量为有界阻塞队列(队列中的锁是分离的,其添加采用的是putLock,移除采用的则是takeLock,两把锁分离,高并发吞吐,线程池在用)
PriorityBlockingQueue    支持按优先级排序的无界阻塞队列
DelayQueue    基于优先级队列(PriorityBlockingQueue)实现的无界阻塞队列(不是先进先出,而是会按照延迟时间的长短来排序,下一个即将执行的任务会排到队列的最前面。入队:不阻塞,无界队列,与PriorityQueue入队相同;
出队:为空时阻塞。不为空时,检查堆顶元素过期时间,小于等于0则出队,大于0说明没过期,则阻塞。)
SynchronousQueue    不存储元素的阻塞队列
LinkedTransferQueue    基于链表结构实现的一个无界阻塞队列
LinkedBlockingDeque    基于链表结构实现的一个双端阻塞队列

Condition类

任意一个Java对象,都拥有一组监视器方法(定义在Object类中),主要包括wait,notify,notifyAll方法,这些方法与synchornized关键字相配合,可以实现等待/通知模式。

Condition接口也提供了类似的Object的监视器方法,与Lock配合可以实现等待/通知模式。但是这两者在使用方式以及功能上还是有差别的。

Condition调用await方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的singal方法,通知当前线程,当前线程才从await方法返回,并且在返回前已经获取了锁

Condition的实现分析

  Condition的实现,主要包括

等待队列:一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程

等待:如果从队列(同步队列和等待队列)的角度来看await方法,调用await方法时,相当于同步队列的首节点移动到了等待队列中

通知:通过调用Condition.singal方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中

JQS

1.AQS和CAS是JAVA并发编程的基石,是JUC构建锁和同步器的框架;
2.AQS的核心是共享变量state和CLH队列(FIFO队列),CLH队列保证资源竞争公平性;
3.AQS资源共享方式包括独占式锁(Exclusive)和共享锁(Share),用于处理不同情况下的资源访问。

AQS 的核心思想是基于一个先进先出(FIFO)的队列,来管理等待获取同步状态的线程。当一个线程获取同步状态失败时,它会被封装成一个节点,然后加入到等待队列中,等待其他线程释放同步状态后再次尝试获取。

JUC总结:

1. Lock 和 ReentrantLock

2. Condition

3. Semaphore(维护了一个计数器,每当一个线程访问该资源时,计数器就会减一;当计数器为零时,所有试图访问该资源的线程都会被阻塞,直到计数器大于零。)

4. CountDownLatch(CountDownLatch 维护了一个计数器,线程调用它的 await() 方法会阻塞,直到计数器变为零)

5. CyclicBarrier(它可以让一组线程在达到某个共同点之前相互等待。CyclicBarrier 维护了一个计数器和一个栅栏点,每当一个线程到达栅栏点时,计数器就会减一;当计数器为零时,所有线程就可以继续执行。)

6 Executors (线程池)

7. Future 和 FutureTask(通过包装方法,获取线程回调结果)

8. ConcurrentHashMap (分段锁接近hashmap线程安全)

9.AQS (AQS 的核心思想是基于一个先进先出(FIFO)的队列,来管理等待获取同步状态的线程。当一个线程获取同步状态失败时,它会被封装成一个节点,然后加入到等待队列中,等待其他线程释放同步状态后再次尝试获取。)

10.ForkJoin (它可以将一个大任务拆分成多个小任务,并行地执行这些小任务,最后将小任务的结果合并成大任务的结果。ForkJoin 框架的核心思想是“工作窃取”,即当一个线程的任务执行完毕后,它可以从其他线程的任务队列中窃取一个任务来执行,以此来实现负载均衡。)

jvm

对象头:

HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

HotSpot虚拟机的对象头(Object Header)包括两部分信息:

第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

第二部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

JVM类加载机制

java的双亲委托机制它的意思是,除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。

JVM内存模型

JVM运行内存的分类
程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有
注:如果正在执行的是Native方法,计数器值则为空
Java虚拟栈:存放基本数据类型、对象的引用、方法出口等,线程私有
本地方法栈:和虚拟栈相似,只不过它服务于Native方法,线程私有
Java堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享
方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。(即永久带),回收目标主要是常量池的回收和类型的卸载,各线程共享

垃圾收集机制(几款不同的收集器)

引用计数算法和可达性分析算法。

GC Roots 大体可以分为三大类,下面这种说法更加好记一些:活动线程相关的各种引用。类的静态变量的引用。JNI 引用。

垃圾收集算法

标记-清除算法(老年代,存在内存空间碎片化问题)

标记-复制算法(so,s1)

标记-整理算法(如果不想浪费空间,就需要有额外空间分配担保,应对被使用内存中所有对象都存活的极端情况,所以老年代一般不使用此算法。)

MinorGC在年轻代空间不足的时候发生,MajorGC指的是老年代的GC,出现MajorGC一般经常伴有MinorGC。

垃圾收集器对比

Serial GC Serial 即串行的意思,也就是说它以串行的方式执行,它是单线程的收集器

Parallel收集器 其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是提高吞吐量

Parallel(new) 标记复制算法

 适用于新生代 Parallel(Old)   标记整理算法、

适用于老年代 CMS 收集器(只适用老年代)

垃圾回收线程几乎能做到与用户线程同时工作。

使用标记-清除算法收集老年代垃圾。

工作流程主要有如下 4 个步骤:

初始标记: 仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿(Stop-the-world)

并发标记: 进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿 重新标记: 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(Stop-the-world)

并发清除: 清理垃圾,不需要停顿 在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。

只有在初始标记,重新标记的时候会进行短暂的停顿。 但 CMS 收集器也有如下缺点: 吞吐量低 无法处理浮动垃圾

标记 - 清除算法带来的内存空间碎片问题 显式的使用该垃圾收集器作为老年代垃圾收集器的方式:-XX:+UseConcMarkSweepGC

G1垃圾收集器

G1从整体看还是基于标记-清除算法的,但是局部上是基于复制算法的。这样就意味者它空间整合做的比较好,因为不会产生空间碎片。

G1 使用了 Region 方式对堆内存进行了划分,且基于标记整理算法实现,整体减少了垃圾碎片的产生。 在初始化标记阶段,搜索可达对象使用到的 Card Table,其实现方式不一样。 G1可以设置一个期望的停顿时间。

JVM调优案例

GC日志详解

spring(spring IOC,spring AOP原理,spring 5,springMVC,事务管理,循环依赖,spring设计模式)

Spring是一个开源的免费的框架(容器)!

Spring是一个轻量级的、非入侵式的框架!

控制反转(IOC),面向切面编程(AOP)!

支持事务的处理,对框架整合的支持!

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法

bean的作用域

Bean的自动装配

自动装配是Spring满足bean依赖的一种方式!

Spring会在上下文中自动寻找,并自动给bean装配属性!

在Spring中有三种装配的方式

在xml中显示的配置

在java中显示配置

隐式的自动装配bean

AOP

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

spring中的事务管理

  • 声明式事务:AOP
  • 编程式事务:需要在代码中,进行事务的管理

事务传播机制

Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。Propagation枚举则引用了这些类型,开发过程中我们一般直接用Propagation枚举。

事务传播行为类型

PROPAGATION_REQUIRED

需要事务(默认)。若当前无事务,新建一个事务;若当前有事务,加入此事务中。

PROPAGATION_SUPPORTS

支持事务。若当前没有事务,以非事务方式执行;若当前有事务,加入此事务中。

PROPAGATION_MANDATORY

强制使用事务。若当前有事务,就使用当前事务;若当前没有事务,抛出异常。

PROPAGATION_REQUIRES_NEW

新建事务。无论当前是否有事务,都新建事务运行。

PROPAGATION_NOT_SUPPORTED

不支持事务。若当前存在事务,把当前事务挂起,然后运行方法。

PROPAGATION_NEVER

不使用事务。若当前方法存在事务,则抛出异常,否则继续使用无事务机制运行。

PROPAGATION_NESTED

嵌套。如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

事务失效

情景1:异常类型错误

声明式事务和注解事务回滚的原理:当被切面切中或者是加了注解的方法中抛出了unchecked exception异常(默认情况)时,Spring会进行事务回滚。unchecked exception异常也就是:RuntimeException及其子类

情景2:自调用

如果在一个类里边,调用同类里边的方法,会导致被调用的方法事务失效

还有如下情况:

方法的权限修饰(@Transactional 注解只能应用到 public 的方法上)

加载配置 (@Transactional 注解开启配置,必须放到listener里加载,如果放到DispatcherServlet的配置里,事务也是不起作用的。)

SpringMVC

SpringMVC的请求流程

1.前端控制器DispatcherServlet。(不需要工程师开发,由框架提供)

详解:用户请求到达前端控制器,它就相当于mvc模式中的c。dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2.处理器映射器HandlerMapping。(不需要工程师开发,由框架提供)

详解:HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3.处理器适配器HandlerAdapter。(不需要工程师开发,由框架提供)

详解:通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4.处理器Handler。(需要工程师开发)

Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。

由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5.视图解析器View resolver。(不需要工程师开发,由框架提供)

作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)

6.视图View。(需要工程师开发)

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

SpringCloud

服务发现与注册Nacos

服务限流熔断降级Sentinel

分布式事务Seata

网关Gateway 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值