首页 > 编程学习 > java综合

java综合

发布时间:2022/10/1 9:10:34

反射相关

类的加载机制

classLoader(双亲委派)加载部分.class->类信息放入元空间->生成唯一Class对象放进堆内存

反射

目的:代码执行时,能够获取类的方法名、属性,以及给属性赋值

原理:Class对象相当于元空间的类信息的镜像,new直接通过元空间的类对象(有指针指向元空间类信息),反射是通过镜像生成对象。

实现:

public class Animal {//default 子类不可跨包, protect可以子类可跨包, private子类可以继承
    public float run(float distance){
       return  distance;
    }
}
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Animal animal1 = new Animal();
        animal1.run(2);

        //类的反射对象
        Class clz = Class.forName("Animal");
        //方法的反射对象
        Method method = clz.getMethod("run", float.class);
        //构造方法的反射对象
        Constructor constructor = clz.getConstructor();

        //通过反射实例化对象
        Object object = constructor.newInstance();
        //通过放射调用方法
        Object distance = method.invoke(object, 3);
    }
}

三种获取Class的方式

  1. 类全限定名获取:Class class = Class.forName(“com.xxxx.xx”);
  2. 具体类获取:Class class = 类.Class;
  3. 对象获取:Class class=object.getClass();

参考:https://www.bilibili.com/video/BV13q4y1s7U8?spm_id_from=333.337.search-card.all.click&vd_source=ccd2ca4db2c7f66c6f0ab6dd058b6208

动态代理

目的:想在所有被代理的方法前加一个方法,动态代理可以方便实现

原理:所有实现被代理对象的方法,都会转为执行invoke中的方法

实现:类似aop

注解

原理:注解只是一个标志,反射查看类信息时发现注解就进行相应操作。

实现:

1.使用Spring中定义好的注解

定义类,使用注解注册以及赋值

@Component
@Data
public class Student {
    @Value("小明")
    private String name;
    @Value("18")
    private int age;
}

定义配置,扫描包范围

@Configuration
@ComponentScan("com.test.bean")
public class MainConfiguration {
}

打印类信息

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
        System.out.println(context.getBean(Student.class));

    }
}

结果

Student(name=小明, age=18)

2.自定义注解实现@Component(spring工厂)以及@Value(spring依赖注入)

注解定义-》注解使用-》@MyComponent表示通过反射生成对象@MyValue表示通过反射赋值(更改属性权限)

注解定义

@Retention(RetentionPolicy.RUNTIME) //运行时使用
@Target(ElementType.TYPE)   //作用于类上
public @interface MyComponent {
}
@Retention(RetentionPolicy.RUNTIME) //运行时使用
@Target(ElementType.FIELD) //作用于属性上
public @interface MyValue {
    String value(); //放置给的属性值
}

类更改为自己的注解

@MyComponent
public class Student {
    @MyValue("小明")
    private String name;
    @MyValue("18")
    private int age;
}

寻找@MyComponet

Class<Student> studentClass = Student.class;
MyComponent myComponent = studentClass.getAnnotation(MyComponent.class);//这里开始只有类,只能找类注解,属性注解需要获取属性后,再获取属性上的注解

实现@MyComponent的方法

if(myComponent!=null){
   Constructor<Student> constructor = studentClass.getConstructor(null);
   Student student = constructor.newInstance();
}

寻找@MyValue

Field[] declareFields = studentClass.getDeclaredFields();// getFields()只能获取公有属性,getDeclaredFields()获取所有属性
for(Field field : declareFields){
    MyValue myValue = studentClass.getAnnotation(MyValue.class);
}

实现@MyValue的方法

if(myValue != null){
     declareField.setAccessible(true); // 变量为private,需要修改属性权限
    // 如果注解中的值为int需要进行转换
    if(declareField.getType().getName().equals("int")){
        declareField.set(student, Integer.parseInt(myValue.value()));//将注解中的值,赋值给创建出的目标对象
    }
    else{
        declareField.set(student, myValue.value());//将注解中的值,赋值给创建出的目标对象
    }
}

完整的测试类

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> studentClass = Student.class;
        MyComponent myComponent = studentClass.getAnnotation(MyComponent.class);
        if(myComponent!=null){
           Constructor<Student> constructor = studentClass.getConstructor(null);
           Student student = constructor.newInstance();

           Field[] declareFields = studentClass.getDeclaredFields();// getFields()只能获取公有属性,getDeclaredFields()获取所有属性
           for(Field declareField : declareFields){
               MyValue myValue = declareField.getAnnotation(MyValue.class);//属性注解,就要在属性上去寻找
               if(myValue != null){
                    declareField.setAccessible(true); // 变量为private,需要修改属性权限
                   // 如果注解中的值为int需要进行转换
                   if(declareField.getType().getName().equals("int")){
                       declareField.set(student, Integer.parseInt(myValue.value()));//将注解中的值,赋值给创建出的目标对象
                   }
                   else{
                       declareField.set(student, myValue.value());//将注解中的值,赋值给创建出的目标对象
                   }
               }
           }
            System.out.println(student);
        }
    }
}

结果

Student(name=小明, age=18)

IOC(@Service,@mapper, BeanFacotory)

目的:将使用new创建对象的高耦合,转变为从容器中获取的低耦合

原理(工厂+xml+反射):加载ApplicationContext容器,从容器中getBean获取

service和mapper举例,直接建立工厂,从工厂中getmapper,降低了service和mapper的耦合,但是增大了service和工厂的耦合。不直接在工厂中new,而是通过xml文件中Bean的id找到mapper的class类,通过反射得到对象。最后,就可以通过getBean(意味着通过xml字符串指定,而不是直接new)得到了,而不是getXXmapper这样的强耦合。

实现:

  1. xml文件
  2. 注解
  3. 注解+扫包

AOP(@Trasaction)

目的:分离业务代码与非业务代码,让主代码块只关心核心业务的编写

原理:Bean的创建过程中,会在BeanPostProcessor中实现

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAjYQzFF-1664516979229)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\QQ截图20220831082221.png)]

实现:

目标对象

public class Student {
    String name;
    int age;
    public void say(){
        System.out.println("天气真好");
    }
}

切面对象

public class AopAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(method);
        System.out.println(args);
        System.out.println(target);
    }
}

xml文件设置代理关系

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean name="student" class="com.test.bean.Student"/>
    <bean name="beforeAdvice" class="com.test.aop.AopAdvice"/>
    <aop:config>
        <aop:pointcut id="stu" expression="execution(* com.test.bean.Student.say())"/>
        <aop:advisor advice-ref="beforeAdvice" pointcut-ref="stu"/>
    </aop:config>
</beans>

主方法

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
        Student student = (Student) context.getBean("student");
        student.say();
    }
}

运行结果

public void com.test.bean.Student.say()
[Ljava.lang.Object;@5884a914
com.test.bean.Student@50378a4
天气真好

拓展:在Spring框架中,PostProcessor也用到了AOP。当一个Bean实现PostProcessor接口后(PosrProcessor.isAssignableFrom(Class)),就实例化一个PostProcessor对象,并将Bean加入到一个列表PostProcessList。在这个列表中的Bean,会根据不同的BeanName添加不同的前置或后置操作。因此,在CreateBean(创建Bean)的方法中,初始化Bean前后会遍历PostProcessList,并调用其中的方法。

spring事务

基本原理

目的:spring使用数据库中的事务

原理:添加@Transactional可自动开启事务,会关闭自动提交,作为Bean,有异常就回滚

实现:

public class TestServiceImp implements TestService{
    TestMapper testMapper;
    @Transactional
    @Override
    public void test() {
        testMapper.insert();
        testMapper.select();
    }
}

如果不使用@Transaction相当于

public class TestServiceImp implements TestService{
    TestMapper testMapper;
    @Override
    public void test() {
        TransactionManager transactionManager;
        try {
            testMapper.insert();
            testMapper.select();
            transactionManager.commit();//提交
        }catch(){
            transactionManager.rollback();//回滚
        }
    }
}

特性

原子性、一致性(数据库保持一致)、隔离性、持久性(写入缓存文件,缓存文件修改后,更新到数据库。如果未写入,可以读取undo log)

持久性:

do log:三种设定:0(commit后值写入Log)、1(默认,commit后直接写入磁盘)、2(commit后写入系统缓存,有系统决定写入磁盘时间)

undo log:有指针指向未进行事务前的状态

隔离级别

两个事务处于并行状态

读另一个未提交(会读取到未提交的insert,同一事务读取到整张表不同的查询结果);读另一个已提交(同一事务读取到另一个update数据,出现幻读);同一事务可重复读保持数据不变(默认,一个事务的commit不影响当前事务的反复查询结果。正因如此,会出现重复插入);串行。

MVCC

数据库中数据多版本的控制

效果:当一个数据正在被修改(update但未提交,此时加了行锁),一个事务过来读取不会阻塞,而是去undo log中读取历史版本(属于快照读,区别与当前读会读取最新版本,insert、upate、delete、select for update会使用当前读),readView控制哪个版本对当前事务可见。

传播机制

A调用B,在B的角度理解

1.REQUIERD:如果A没有事务就新建,A有事务B就加入

2.SUPPORTS:如果A没有事务,B就以非事务方式执行

SpringBoot

自动装配

spring starter

起步依赖:

会导入其余和某个依赖相关的Jar包

自动装配:

工厂、容器->配置导入->EableAutoConfiguration

@Configuration让spring IOC容器管理Bean的实例工厂

@Bean返回的对象注册到spring容器中(Bean的生命周期?)

@ConfigurationProperties将配置参数信息yaml、properties文件导入到Bean中

@EnableAutoConfiguration让Bean能够被发现

BeanFacory和FactoryBean(定制Bean的生产过程)

Bean的生命周期

实例化->属性填充(三级缓存)->调用Aware相关(完成BeanFactory,BeanName, BeanClassLoader设置)->postProcessor前置->postProcessor后置->注销

简单Bean周期:

扫描

每个Beand对应一个BeanDefinetion对象保存Bean属性(Class, scope)。产生原因是通过一个字符串找到类困难,并且多次创建需要多次解析麻烦。

1.实例化

2.填充属性(对象)

3.填充属性

4.Aop

5.放入单例池(ConcurrentHashMap<BeanName, 对象>)

单例模式下的Bean循环依赖问题:

Aservice需要Bservice ,Bservice需要Aservice,但是此时单例池中还未放入

一级缓存:单例池

二级缓存:Aop完成的对象,防止B、C均需要单例A,创建了两次,因此B完成Aop后放入二级缓存,C再创建A时就需要进入二级缓存找。

三级缓存:当二级缓存中没有,需要Aop时,需要原始对象,来自上一步实例化时使用lambda表达式将BeanName,BeanDefinition(是否需要aop)到三级缓存,这样执行lambda表达式时相当于进行了Aop。

数据库相关

MySql

原理

show index form student;(查看索引)

索引添加:主键、唯一会添加索引、key()给指定字段添加索引

select * from student where id=“1”;(id有索引查询很快)

select * from student where name=“xiaomiao”;(name没有索引查询很慢)

提高查询效率,降低了写操作,但无所谓,大部分为查询

索引类型

普通(有重复数据)、唯一、主键(特殊唯一、唯一标识一条记录)、组合、全文(解决是否包含)

普通索引:叶子节点存储的是主键索引

全文索引:

组合索引:最左前缀原则:(name,age,sex)组成索引后,相当于建立了name;name、age;name、age、sex(严格从左到右建立的索引,根据name建立的age,根据age建立的sex)。查询(“aaa”,18,“女”)的位置,会先查找到相同的name,再查找age、sex。如果只查找age=18,sex=“男”会出现宕机,因为是根据name建立的索引。

索引失效

模(模糊查询like不用%)型(类型错误)数(内部函数)空(不限制Not null)运(索引列±*/)最(最左缀)快(全表扫描更快)

聚集索引、非聚集索引

主键为聚簇索引(行数据放在叶子节点),非主键为非聚簇索引。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JAKoZFjH-1664516979232)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\聚簇、非聚簇索引.png)]

left join right join inner join

结果集去重

distinct a,b,c ;gounpby a,b,c

sql优化

什么样的sql需要优化-》慢查询日志设置查询超过5s-》EXPLAIN 找执行计划(type:system>const>ref>all,rows:查询的行数)-》索引失效

redis

参考:https://www.bilibili.com/video/BV1aU4y1Z71c?p=1&vd_source=ccd2ca4db2c7f66c6f0ab6dd058b6208

数据结构

使用C语言编写的redis

字符串:由于C语言中字符串长度查询为O(n),无法存特殊字符,扩容不便。redis中字符串使用struct封装,并添加长度等属性。

哈希表:链地址法,链中存储entry为键值对

集合:哈希表的键唯一

有序集合:依据为链表,在链表中建立二级、三级…索引形成level

缓存

缓存击穿:一条数据缓存中没有,要到数据库中查询。设置锁拦截。

缓存雪崩:多条数据缓存中没有。设置锁,热点数据提前更新到缓存。

缓存穿透:多次查询缓存、数据库中均没有的数据。布隆方法为每个数据库的数据,利用多个hash计算出1、0组成的数组,查询前数据通过相同hash计算出的数组值是否符合,过滤掉大量无效查询。

缓存淘汰:LRU

过期删除:防止一直读旧数据。主动删除(一段时间后删除所有)+惰性删除(一般情况下,查看数据是否过期,过期去数据库找)

缓存一致:数据库可以利用双删。在写入操作前后均删除缓存。防止只在写入前进行删除后,有线程把旧数据读到缓存中,导致后序线程均从缓存中读到旧数据。

集群

主从复制:主库复制写,从库负责读。从库可以全量复制主库中数据,一般只复制近期主库中执行的操作。

哨兵:主库、从库会每个10s向哨兵发送自己的状态。主库出现问题,哨兵会挑选出主库。

cluster:不同服务器存储不同的数据(主库、从库数据相同),会有槽位信息记录在每台服务器上。

并行计算相关

多线程

https://www.bilibili.com/video/BV1V4411p7EF?p=1

线程生命周期(5个):

创建(new)—》就绪(start后)《-》阻塞(调用wait()/sleep()会切换)《-》运行(start后并在cpu请求到资源)-》死亡

多线程创建方式:

继承Thread(同样实现了Runable接口,因此可以用静态代理的方式,传入Ruable对象,只实现了Run方法的Ruable类可以用匿名内部类进行创建,进一步使用lambda表达式精简)实现Runable(主要采取此方式,可以传入多个线程)、Callable(提供返回值,以及开启结束服务)

抢票:

public class Test {

     static class  GetTicket implements  Runnable{
        static int ticketNum = 10;
        @Override
        public void run() {
            while (true){
                if(ticketNum<=0)break;
                System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--+"号票");
            }
        }
    }

    public static void main(String[] args) {
        GetTicket getTicket = new GetTicket();
        Thread t1 = new Thread(getTicket,"学生");
        Thread t2 = new Thread(getTicket,"老师");
        Thread t3 = new Thread(getTicket,"黄牛");

        t1.start();
        t2.start();
        t3.start();
    }

线程设置

线程停止:建议设置标志位

public class Test_stop implements Runnable{
    //标志位
    boolean flag = true;
    @Override
    public void run() {
        while (flag){
            System.out.println("线程进行中");
        }
    }
    //改变标志位的方法
    public void changeFlag(boolean flag){
        this.flag=flag;
    }

    public static void main(String[] args) {
        Test_stop test_stop = new Test_stop();
        new Thread(test_stop).start();
        
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
            if(i==20){test_stop.changeFlag(false); }//当i==20时test_stop线程结束
        }
    }
}

休眠:Thread.sleep()

礼让:Thread.yied()回到同一起跑线,不一定成功,取决于cpu

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
        //礼让成功结果为:a开始 b开始 a结束 b结束
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始");
        Thread.yield();//如果没有礼让成功,就会将一个线程从开始到结束执行完
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}

线程强制执行Thread.join(),加入进的线程要执行完

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("VIP来了,执行第"+i+"次");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main执行第"+i+"次");
            if(i==100){
                thread.join();//强势介入,main在100之后就要等thread执行完才能执行
            }
        }
    }
}

Thread.status线程状态(死亡后不能再次启动)

Thread.setPriority,范围1-10,默认为5

Thread.setDaemon,虚拟机不会等待守护线程结束

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread godThread = new Thread(god);
        godThread.setDaemon(true);//设置守护
        godThread.start();//守护线程

        new Thread(you).start();//当该线程结束后,虚拟机还会将守护线程再跑一会
    }
}

class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("开心活着"+i+"年");
        }
    }
}

class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝守护者");
        }
    }
}

锁住某一段代码,提供一个对象作为锁的本身。锁住原理是获取每个对象的moniter监视器

重排序:代码指令会调整顺序,由于重排序,下列代码可能输出B,因为可能将b=1放到a=1前面。

public class Main {
    private static int a=0;
    private static int b=0;
    public static void main(String[] args) throws FileNotFoundException {
        new Thread(()->{
                a=1;
                b=1;
        }).start();
        new Thread(()->{
            if(b==1){
                if(a==1){
                    System.out.println("A");
                }
                else {
                    System.out.println("B");
                }
            }
        }).start();
    }
}

volatile:

解决问题:对所有线程保持可见性,会将变量放到主内存

不保证原子性,还是会出现两个线程同时操作。

防止指令重排(之前的指令执行,之后的指令不执行)

sychonized底层实现(对象锁,传入的类.class实际就是个Class对象,联想到object具有的几个方法Clone()(浅克隆,只克隆地质),toString(),wait()(释放锁,sleep不释放锁),notify(),equals(), hashcode(),finalize()(释放资源)。当sychornized锁方法的时候,默认锁住this对象。

抢票:

public class Test {
     static class  GetTicket implements  Runnable{
        static int ticketNum = 10;
        @Override
        public synchronized void run() {//加入synchornized关键字防止同一张票多人抢、抢到负数
            while (true){
                if(ticketNum<=0)break;
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+"抢到了"+ticketNum--+"号票");
            }
        }
    }

    public static void main(String[] args) {
        GetTicket getTicket = new GetTicket();
        Thread t1 = new Thread(getTicket,"学生");
        Thread t2 = new Thread(getTicket,"老师");
        Thread t3 = new Thread(getTicket,"黄牛");

        t1.start();
        t2.start();
        t3.start();
    }
}

银行取钱:

public class TestBank {
    public static void main(String[] args) {
        Account account = new Account(50);
        DrawMoney you = new DrawMoney(account, 50, "you");
        DrawMoney girlfriend = new DrawMoney(account, 50, "girlfriend");

        you.start();
        girlfriend.start();
    }
}
class Account{
    int money=1000;
    public Account(int money){
        this.money=money;
    }
}

class DrawMoney extends Thread{
    Account account;
    int myMoney;
    int drawMoney;

    public DrawMoney(Account account,int drawMoney,String name){
        super(name);
        this.drawMoney=drawMoney;
        this.account=account;
    }
    @Override
    public void run() {//在这里加synchronized无用,锁的是DrwMoney,还是会操作未加锁的account,需要使用同步块
        synchronized (account){
            if(account.money<=0){
                //extends Thread因此this.name==Thread.currentThread.getName()
                System.out.println("钱不够了,"+this.getName()+"取不了");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //取钱
            account.money = account.money-drawMoney;
            System.out.println(this.getName()+"取走了"+drawMoney+"还剩:"+account.money);
        }
    }
}

线程不安全ArrayList:CopyOnWriteArrayList会复制个transient数组(不被序列化,只在内存中)写,并锁住add操作,此时另外线程会读取旧数组

public class TestArrayList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> strings = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                strings.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);//这里必须休息,不然主线程跑完了打印出的string.size是不完整的
        System.out.println(strings.size());//大小不足1000,因为不同线程会写入同一位置,CopyOnWriteArrayList就是1000
    }
}

死锁:

互相持有对方的锁,jps->jstack查找,以下代码不会结束

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        Object o1 = new Object();
        Object o2 = new Object();
        Thread th1 = new Thread(()->{
       synchronized (o1){
           try {
               Thread.sleep(1000);//并未释放o1锁
               synchronized (o2){
                   System.out.println("线程1结束");
               }
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
   });
            Thread th2 = new Thread(()->{
                synchronized (o2){
                    try {
                        Thread.sleep(1000);
                        synchronized (o1){
                            System.out.println("线程2结束");
                        }
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }

                }

   });
    th1.start();
    th2.start();
    }
}

lock、condition:下列代码执行顺序:线程1进入等待->线程2唤醒其它线程->线程2结束->线程1等待结束

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
   Lock lock = new ReentrantLock();
   Condition condition = lock.newCondition();//可以给锁变换状态
   new Thread(()->{
       lock.lock();//必须持有锁才能进行wait
       System.out.println("线程1进入等待");
       try {
           condition.await();
       } catch (InterruptedException e) {
           throw new RuntimeException(e);
       }
       System.out.println("线程1等待结束");
   }).start();
   new Thread(()->{
        lock.lock();
       System.out.println("线程2唤醒其它线程");
       condition.signal();
       System.out.println("线程2结束");
       lock.unlock();
   }).start();
    }
}

ABA问题:

修改值时,只会比较当前值和之前值是否一致,一致就进行修改。但是会出现另一个线程已经重新赋相同的值。

juc解决方法:记录版本号

AQS:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8bzIuQTs-1664516979235)(C:\Users\DeLL\AppData\Roaming\Typora\typora-user-images\image-20220927151614499.png)]

几种锁状态的切换

无锁->偏向锁(对象记录线程的指针)->自旋(出现其余锁竞争)->轻量级锁(对象记录线程Lock Record指针)->重量级锁(大量的自旋消耗cpu资源,不如将轻量级锁改为重量级锁,其余线程为wait()不消耗cpu)

轻量级锁:对象中有mark word,虚拟栈中有Lock Record。CAS:当对象中的Mark Word为空时,创建Lock Record,并将mark word填入自己的Lock Record(diaplaced mark word)。不为空时,拿到mark word中的值,与自己的Lock Record进行比较,如果一样,就执行同步代码块,如果不一样就说明已经有线程占用,升级为重量级锁。

线程池

public class Main {
    public static void main(String[] args) throws FileNotFoundException, InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4,//核心2,最大4,意味这最多再新创建2个线程
                1, TimeUnit.SECONDS,//非核心存活1s
                new ArrayBlockingQueue<>(2));//等待2
        //最大可以塞满8个线程,因为有2个线程在队列中,则同时可以执行6个线程
        for (int i=0; i<6; i++){
            int finalI = i; //当前任务
            executor.execute(()->{
                System.out.println("线程:"+Thread.currentThread().getName()+"执行任务:"+finalI);
                try {
                    TimeUnit.SECONDS.sleep(1);//Thread.sleep()的替身
                    System.out.println("线程:"+Thread.currentThread().getName()+"结束任务:"+finalI);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            });
        }
        System.out.println("线程数量:"+executor.getPoolSize());//线程数量
        TimeUnit.SECONDS.sleep(3);//等待所有执行完
        System.out.println("线程数量:"+executor.getPoolSize());
        executor.shutdown();
    }
}

运行结果

线程数量:4
线程:pool-1-thread-1执行任务:0
线程:pool-1-thread-4执行任务:5
线程:pool-1-thread-2执行任务:1
线程:pool-1-thread-3执行任务:4
线程:pool-1-thread-1结束任务:0
线程:pool-1-thread-4结束任务:5
线程:pool-1-thread-1执行任务:2
线程:pool-1-thread-4执行任务:3
线程:pool-1-thread-2结束任务:1
线程:pool-1-thread-3结束任务:4
线程:pool-1-thread-1结束任务:2
线程:pool-1-thread-4结束任务:3
线程数量:2

原理

频繁矿建线程,浪费资源

核心线程-》等待队列-》创建新线程-》拒绝

线程池种类

JVM相关

原理

内存模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LX2RibEF-1664516979236)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型.png)]

jvm栈会为每个函数开辟空间,函数结束运行后清除该空间,但是不能清除堆中空间,因此使用到GC

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kxr5hPH-1664516979238)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型值.png)]

引用类型也是值传递,不过传递的是地址(int)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XK8iBstF-1664516979239)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\内存模型引用类型.png)]

垃圾回收

什么对象会被回收(不会再使用)?

引用计数:有一个地方引用+1;离开作用域或设置为null-1,当为0时引用失效。但循环引用a=b=null时,虽然两者均无用,但相互引用让计数器不会为0。

可达性:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wxw9BsuT-1664516979241)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\GC可达性算法.png)]

GCroots:

jvm栈中引用(正在使用)、元空间引用(全局)、本地方法栈引用(c++)。堆区对象和GCroots有直接或间接(被GCroots引用的对象引用)联系,就不能被删除。

分代管理:经过几次垃圾回收,都没被判定为可回收,就放在一个区域不再进行回收。

如何进行回收?

标记-》清除:标记清除后连续内存出现空隙(CMS老年代)

标记-》复制:内存空间分为两半,存活对象复制到另一半,解决了空隙问题。但如果大量对象为存活对象(回收不了多少),浪费了空间。(parNew用于新生代)

标记-》整理:将存活对象放在前面,回收对象放在后面,每次清除后面。解决了空闲浪费,但修改对象位置,效率低。

基础问题

byte 1;short 2;int 4;long 8;float 4;double 8;boolean 1;char 2

集合类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivZ1e3zp-1664516979242)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\集合.png)]

arraylist:可以调整大小的数组实现

堆栈实现

hashset

利用hash值判断位置,再比对值。

concrrentHashMap线程安全实现

hash冲突的解决方法:再哈希;链地址(hashmap使用的方法,冲突后变成链表);开放地址(探索周围位置);公共溢出区

hash线程安全

abstract和interface

abstract: is a,只能继承一个,可以有私有变量与方法

interface: like a, 可以实现多个,不能又私有变量与方法

==和equals

public class TestEquals {
    public static void main(String[] args) {
        String a = "123";
        String b = new String("123");
        String c = "123";
        System.out.println(a==b); //false, 此时比较对象地址
        System.out.println(a==c);//true, 此时比较值
        System.out.println(a.equals(b));//true 此时比较值
        System.out.println(a.equals(c));//true 此时比较值
    }
}

B树、B+树

B树是基于二叉搜索树而来(非叶子节点有数据),B+树是基于分块思想而来。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqJiNyFM-1664516979243)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\B树.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjcgU8Wm-1664516979244)(C:\Users\DeLL\Desktop\文档\就业资料\java面试资料\java总结\B+树.png)]

单例模式实现方式

选择排序、插入排序

记-》整理:将存活对象放在前面,回收对象放在后面,每次清除后面。解决了空闲浪费,但修改对象位置,效率低。

基础问题

byte 1;short 2;int 4;long 8;float 4;double 8;boolean 1;char 2

集合类

[外链图片转存中…(img-ivZ1e3zp-1664516979242)]

arraylist:可以调整大小的数组实现

堆栈实现

hashset

利用hash值判断位置,再比对值。

concrrentHashMap线程安全实现

hash冲突的解决方法:再哈希;链地址(hashmap使用的方法,冲突后变成链表);开放地址(探索周围位置);公共溢出区

hash线程安全

abstract和interface

abstract: is a,只能继承一个,可以有私有变量与方法

interface: like a, 可以实现多个,不能又私有变量与方法

==和equals

public class TestEquals {
    public static void main(String[] args) {
        String a = "123";
        String b = new String("123");
        String c = "123";
        System.out.println(a==b); //false, 此时比较对象地址
        System.out.println(a==c);//true, 此时比较值
        System.out.println(a.equals(b));//true 此时比较值
        System.out.println(a.equals(c));//true 此时比较值
    }
}

B树、B+树

B树是基于二叉搜索树而来(非叶子节点有数据),B+树是基于分块思想而来。

[外链图片转存中…(img-VqJiNyFM-1664516979243)]

[外链图片转存中…(img-tjcgU8Wm-1664516979244)]

单例模式实现方式

选择排序、插入排序

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号