知识整理(二)

目录

1.聚簇索引、覆盖索引

2.jvm内存模型

3.ioc,aop

4.jdk lock接口实现类

5.springboot 整合starter过程

6.线程start两次

7.jvm界面工具都有那些

8.fork/join框架

9.树的层序遍历

10.泛型

11.垃圾回收器新生代收集器:Serial、ParNew、Parallel Scavenge

12.spring 事务处理中,同一个类中:A方法(无事务)调B方法(有事务),事务不生效问题

13.overload和override是多态的表达。

14.单例模式安全 双重校验锁

15.concurrenthashmap的数据结构

16.使用gc的作用

17.cpu过高原因

18.sleep和wait区别

19.什么是类加载器,类加载器是怎么工作的

20.手写二分查找算法

21.手写生产者,消费者

22.innodb和myIsam的区别

23.tcp/IP四层

24.怎么减少fullgc

25线程池大小设置,CPU的核心数、线程数的关系和区别

26.各内存存储数据

27.java中的自动封箱的作用


 

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

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值