目录
11.垃圾回收器新生代收集器:Serial、ParNew、Parallel Scavenge
12.spring 事务处理中,同一个类中:A方法(无事务)调B方法(有事务),事务不生效问题
1.聚簇索引、覆盖索引
https://blog.csdn.net/u013132035/article/details/82193763
2.jvm内存模型
3.ioc,aop
IOC控制反转,奖bean对象交给spring进行管理
Aop 切面编程,jdk动态代理的机制,前置通知,后置通知,异常通知,环绕通知。
切面,切点,通知。
4.jdk lock接口实现类
reentrantLock 重入锁
5.springboot 整合starter过程
@Configuration&与@Bean------>>>基于java代码的bean配置
@Conditional-------->>>>>>设置自动配置条件依赖
@EnableConfigurationProperties与@ConfigurationProperties->读取配置文件转换为bean。
@EnableAutoConfiguration、@AutoConfigurationPackage 与@Import->实现bean发现与加载。
6.线程start两次
一个线程对象只能调用一次start方法.从new到等待运行是单行道,所以如果你对一个已经启动的线程对象再调用一次start方法的话,会产生:IllegalThreadStateException异常.
可以被重复调用的是run()方法。
Thread类中run()和start()方法的区别如下:
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程
7.jvm界面工具都有那些
8.fork/join框架
9.树的层序遍历
广度优先算法
树的遍历
https://blog.csdn.net/Applying/article/details/84982712
10.泛型
public static <T> List<T> jsonToList(String jsonString, Class<T> clazz) {
@SuppressWarnings("unchecked")
List<T> ts = (List<T>) JSONArray.parseArray(jsonString, clazz);
return ts;
}
11.垃圾回收器
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old 老年代收集器 采用标记整理,标记清除
整堆收集器: G1 堆收集器
12.spring 事务处理中,同一个类中:A方法(无事务)调B方法(有事务),事务不生效问题
13.overload和override是多态的表达。
14.单例模式安全 双重校验锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
15.concurrenthashmap的数据结构
并发编程实践中,ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求(这点好像CAP理论啊 O(∩_∩)O)。ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响,无论对于Java并发编程的学习还是Java内存模型的理解,ConcurrentHashMap的设计以及源码都值得非常仔细的阅读与揣摩。
数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。ConcurrentHashMap中的HashEntry相对于HashMap中的Entry有一定的差异性:HashEntry中的value以及next都被volatile修饰,这样在多线程读写过程中能够保持它们的可见性,代码如下:
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
16.使用gc的作用
java对内存的释放采取的垃圾自动回收机制,在编程的时候不用考虑变量不用时释放内存,java虚拟机可以自动判断出并收集到垃圾,但一般不会立即释放它们的内存空间,当然也可以在程序中使用System.gc()来强制垃圾回收,但是要注意的是,系统并不保证会立即进行释放内存
6jvm设置堆大小
-Xmx:最大堆大小
-Xms:初始堆大小
-Xmn:年轻代大小
-XXSurvivorRatio:年轻代中Eden区与Survivor区的大小比值
年轻代5120m, Eden:Survivor=3,Survivor区大小=1024m(Survivor区有两个,即将年轻代分为5份,每个Survivor区占一份),总大小为2048m。
-Xms初始堆大小即最小内存值为10240m
java -Xmx128m -Xms128m -Xmn64m -Xss1m
-Xmx128m:设置JVM最大可用内存为128M。
-Xms128m:设置JVM最小内存为128m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更 多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
17.cpu过高原因
个应用占用CPU很高,除了确实是计算密集型应用之外,通常原因都是出现了死循环。
以我们最近出现的一个实际故障为例,介绍怎么定位和解决这类问题。
1.top 查到pid 28555
2.ps aux|grep 28555 确定到是tomcat的进程
3.显示线程列表 ps -mp 28555 -o THREAD,tid,time 查到tid 28802
4. printf "%x\n" 28802 将线程id,tid进行16进制转换
5.jstack pid |grep tid -A 30 显示堆栈信息 jstack 28555 |grep 28802 -A 30
18.sleep和wait区别
sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复,调用sleep 不会释放对象锁。由于没有释放对象锁,所以不能调用里面的同步方法。
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);可以调用里面的同步方法,其他线程可以访问;
19.什么是类加载器,类加载器是怎么工作的
类加载器是一个用来加载类文件的类。Java源代码通过javac编译器编译成类文件。然后JVM来执行类文件中的字节码来执行程序。类加载器负责 加载文件系统、网络或其他来源的类文件。有三种默认使用的类加载器:Bootstrap类加载器、Extension类加载器和System类加载器(或 者叫作Application类加载器)。每种类加载器都有设定好从哪里加载类。
1) Bootstrap类加载器 – JRE/lib/rt.jar
2) Extension类加载器 – JRE/lib/ext或者java.ext.dirs指向的目录
3) Application类加载器 – CLASSPATH环境变量, 由-classpath或-cp选项定义,或者是JAR中的Manifest的classpath属性定义.
20.手写二分查找算法
package com.example.demo;
/**
* @Author: hanzl
* @Date: 2019/10/20 上午9:24
* @Version 1.0
*/
public class BinarySearch {
public static void main(String args[]){
int a[]={1,2,5,8,10};
int index=binarySearch(a,11);
System.out.print(index);
}
public static int binarySearch(int a[],int key){
int len=a.length;
int start=0;
int end=len-1;
if(start>end){
return -1;
}
while(start<=end){
int middle=(start+end)/2;
if(key<a[middle]){
end=middle-1;
}else if(key>a[middle]){
start=middle+1;
}else {
return middle;
}
}
return -1;
}
}
21.手写生产者,消费者
生产者
package com.example.demo.produceAndcosumer;
import java.util.Queue;
import java.util.Random;
/**
* @Author: hanzl
* @Date: 2019/10/20 上午9:35
* @Version 1.0
*/
public class Producer extends Thread {
//指定队列
private Queue<Integer> queue;
//生产者名称
private String name;
//队列的最大值
private int maxSize;
//变量
private int i=0;
public Producer(String name,Queue<Integer> queue,int maxSize){
this.queue=queue;
this.maxSize=maxSize;
this.name=name;
}
@Override
public void run() {
while (true){
synchronized (queue){
while(queue.size()==maxSize){
try {
System.out .println("Queue is full, Producer[" + name + "] thread waiting for " + "consumer to take something from queue.");
queue.wait();
}catch (Exception e){
e.printStackTrace();
}
}
System.out.println("[" + name + "] Producing value : +" + i);
queue.offer(i++);
queue.notifyAll();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
消费者
package com.example.demo.produceAndcosumer;
import java.util.Queue;
import java.util.Random;
/**
* @Author: hanzl
* @Date: 2019/10/20 上午9:44
* @Version 1.0
*/
public class Consumer extends Thread{
//指定队列
private Queue<Integer> queue;
//生产者名称
private String name;
//队列的最大值
private int maxSize;
//变量
private int i=0;
public Consumer(String name,Queue<Integer> queue,int maxSize){
this.queue=queue;
this.maxSize=maxSize;
this.name=name;
}
@Override
public void run() {
while(true){
synchronized (queue){
while(queue.isEmpty()){
try {
System.out.println("Queue is empty, Consumer[" + name + "] thread is waiting for Producer");
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
int x = queue.poll();
System.out.println("[" + name + "] Consuming value : " + x);
queue.notifyAll();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
测试
package com.example.demo.produceAndcosumer;
import java.util.LinkedList;
import java.util.Queue;
/**
* @Author: hanzl
* @Date: 2019/10/20 上午9:52
* @Version 1.0
*/
public class TestProducerAndCosumer {
private static final int CAPACITY = 5;
public static void main(String args[]){
Queue<Integer> queue = new LinkedList<Integer>();
Thread producer1 = new Producer("P-1", queue, CAPACITY);
Thread producer2 = new Producer("P-2", queue, CAPACITY);
Thread consumer1 = new Consumer("C1", queue, CAPACITY);
Thread consumer2 = new Consumer("C2", queue, CAPACITY);
Thread consumer3 = new Consumer("C3", queue, CAPACITY);
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
consumer3.start();
}
}
package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerAndCustomer {
private int count;
public final int MAX_COUNT = 10;
ReentrantLock reentrantLock = new ReentrantLock();
Condition push = reentrantLock.newCondition();
Condition take = reentrantLock.newCondition();
public void push() {
reentrantLock.lock();
while (count >= 10) try {
System.out.println("库存大于等于10个,阻塞停止生产!");
push.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName() + "生产者生产,库存:" + count);
take.signal();
reentrantLock.unlock();
}
public void take() {
reentrantLock.lock();
while (count <= 0) {
try {
System.out.println("拿的太快啦,收手停止一下啦!");
take.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println("有人偷偷拿走一个商品,还剩:" + count);
push.signal();
reentrantLock.unlock();
}
public static void main(String[] args) {
ProducerAndCustomer producerAndCustomer = new ProducerAndCustomer();
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Producer(producerAndCustomer));
executorService.execute(new Producer(producerAndCustomer));
executorService.execute(new Producer(producerAndCustomer));
executorService.execute(new Producer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
executorService.execute(new Customer(producerAndCustomer));
}
}
class Producer implements Runnable {
private ProducerAndCustomer producerAndCustomer;
public Producer(ProducerAndCustomer producerAndCustomer) {
this.producerAndCustomer = producerAndCustomer;
}
@Override
public void run() {
while (true) {
producerAndCustomer.push();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Customer implements Runnable {
private ProducerAndCustomer producerAndCustomer;
public Customer(ProducerAndCustomer producerAndCustomer) {
this.producerAndCustomer = producerAndCustomer;
}
@Override
public void run() {
while (true) {
producerAndCustomer.take();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
22.innodb和myIsam的区别
1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
3. InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
MyISAM是非聚集索引,也是使用B+Tree作为索引结构,索引和数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
也就是说:InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针。
4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
5. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;5.7以后的InnoDB支持全文索引了
6. MyISAM表格可以被压缩后进行查询操作
7. InnoDB支持表、行(默认)级锁,而MyISAM支持表级锁
InnoDB的行锁是实现在索引上的,而不是锁在物理行记录上。潜台词是,如果访问没有命中索引,也无法使用行锁,将要退化为表锁。
8、InnoDB表必须有主键(用户没有指定的话会自己找或生产一个主键),而Myisam可以没有
9、Innodb存储文件有frm、ibd,而Myisam是frm、MYD、MYI
Innodb:frm是表定义文件,ibd是数据文件
Myisam:frm是表定义文件,myd是数据文件,myi是索引文件
23.tcp/IP四层
24.怎么减少fullgc
扩大老年代,扩大方法区,禁止使用system.gc()
25线程池大小设置,CPU的核心数、线程数的关系和区别
- IO密集型
- CPU密集型
简单的分析来看,如果是CPU密集型的任务,我们应该设置数目较小的线程数,比如CPU数目加1。如果是IO密集型的任务,则应该设置可能多的线程数,由于IO操作不占用CPU,所以,不能让CPU闲下来。当然,如果线程数目太多,那么线程切换所带来的开销又会对系统的响应时间带来影响。
在《linux多线程服务器端编程》中有一个思路,CPU计算和IO的阻抗匹配原则。
如果线程池中的线程在执行任务时,密集计算所占的时间比重为P(0<P<=1),而系统一共有C个CPU,为了让CPU跑满而又不过载,线程池的大小经验公式 T = C / P。在此,T只是一个参考,考虑到P的估计并不是很准确,T的最佳估值可以上下浮动50%。
这个经验公式的原理很简单,T个线程,每个线程占用P的CPU时间,如果刚好占满C个CPU,那么必有 T * P = C。
下面验证一下边界条件的正确性:
假设C = 8,P = 1.0,线程池的任务完全是密集计算,那么T = 8。只要8个活动线程就能让8个CPU饱和,再多也没用了,因为CPU资源已经耗光了。
假设C = 8,P = 0.5,线程池的任务有一半是计算,有一半在等IO上,那么T = 16.考虑操作系统能灵活,合理调度sleeping/writing/running线程,那么大概16个“50%繁忙的线程”能让8个CPU忙个不停。启动更多的线程并不能提高吞吐量,反而因为增加上下文切换的开销而降低性能。
26.各内存存储数据
方法区:类信息、类变量(静态变量和常量)、方法
堆:对象、成员变量
栈:局部变量
(1)当程序运行时,首先通过类装载器加载字节码文件,经过解析后装入方法区!在方法区中存了类的各种信息,包括类变量、类常量及方法。对于同一个方法的调用,同一个类的不同实例调用的都是存在方法区的同一个方法。类变量的生命周期从程序开始运行时创建,到程序终止运行时结束!
(2)当程序中new一个对象时,这个对象存在堆中,对象的变量存在栈中,指向堆中的引用!对象的成员变量都存在堆中,当对象被回收时,对象的成员变量随之消失!
(3)当方法调用时,JVM会在栈中分配一个栈桢,存储方法的局部变量。当方法调用结束时,局部变量消失!
类变量:属于类的属性信息,与类的实例无关,多个实例共用同一个类变量,存在与方法区中。类变量用static修饰,包括静态变量和常量。静态变量有默认初始值,常量必须声明同时初始化。
成员变量:属于实例的变量,只与实例有关,写在类下面,方法外,非static修饰。成员变量会随着成员的创建而生存,随着成员的回收而销毁。
局部变量:声明在方法中,没有默认初始值,随着方法的调用而创建,存储于栈中,随着方法调用的结束而销毁。
27.java中的自动封箱的作用
基本类型数据包装到对象中,作为对象来使用,这是封箱,这个封箱过程由java自动实现,就叫做自动封箱。
28.not in和not exis区别
not in会扫描全表
29.怎么停止线程池
30.threadLocal
31.integer值的比较
128到127之间不会封装对象而是用常量池的值,不在这个范围才会创建对象
32.Java单例模式双重检查锁定中volatile关键字的作用
volatile作用:以下会涉及到Java内存模型的知识
禁止指令重排序。我们知道new Singleton()是一个非原子操作,编译器可能会重排序【构造函数可能在整个对象初始化完成前执行完毕,即赋值操作(只是在内存中开辟一片存储区域后直接返回内存的引用)在初始化对象前完成】。而线程B在线程A赋值完时判断instance就不为null了,此时B拿到的将是一个没有初始化完成的半成品。
保证可见性。线程A在自己的工作线程内创建了实例,但此时还未同步到主存中;此时线程B在主存中判断instance还是null,那么线程B又将在自己的工作线程中创建一个实例,这样就创建了多个实例。
顺便提一下,volatile禁止指令重排序只能保证volatile修饰的代码之后的代码不会在它之前执行
33.事务的四种隔离级别
34. 字符串“==”比较
String a="abc";和String b="abc" 对于这样的类型的声明方式(不是new出来的)串池把他们看做一个对象,所以池中只存储一分,只是有a 和b 两个引用罢了!
对于String只要用new关键字new出来的string,都是单独的一个对象。
而“==”号,他是用来比较对象的内存地址,所以只要用==号来比较string,只要不是自己比自己,那肯定是false。
35.HashMap中的为什么hash的长度为2的幂而&位必须为奇数
因为 n 永远是2的次幂,所以 n-1 通过 二进制表示,永远都是尾端以连续1的形式表示(00001111,00000011)
当(n - 1) 和 hash 做与运算时,会保留hash中 后 x 位的 1,
例如 00001111 & 10000011 = 00000011
这样做有2个好处
&运算速度快,至少比%取模运算块
能保证 索引值 肯定在 capacity 中,不会超出数组长度
(n - 1) & hash,当n为2次幂时,会满足一个公式:(n - 1) & hash = hash % n
https://blog.csdn.net/u010841296/article/details/82832166
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648到2147483647,前后加起来大概40亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是“ (n - 1) & hash ”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。这个算法应该如何设计呢?我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。
这是表面原因,%len和&(len-1)效率问题其实差距不大,也就是4倍左右的差距;2^n的核心原因是hash函数的源码中右移了16位让低位保留高位信息,原本的低位信息不要,那么进行&操作另一个数低位必须全是1,否则没有意义,所以len必须是2^n