面试总结2

news2024/10/2 1:40:00

用时:40min

  • 自我介绍
  • hashset存储对象怎么进行判断是否重复
  • Synchronized底层实现原理

  • Synchronized锁的升级过程

  • 讲讲开发中常用到的spring注解
  • spring事务,传播机制,隔离级别
  • 说一说你常用的git命令
  • 说一说你对IOC、AOP的理解

1、hashset存储对象怎么进行判断是否重复

HashSet  根据每个对象的哈希码值(调用hashCode()获得)用固定的算法算出它的存储索引,把对象存放在一个叫散列表的相应位置(表元)中:

  • 存对象时,hashSet集合首先调用该对象的hashCode方法来获得该对象的hashCode值,与hash表中的值进行比较。如果不存在,则直接把该对象存入集合中,并把该hashCode值存入hash表中,此次add操作结束。如果存在,则进行下面的计算。

  • 通过”==”操作符判断已经存入的对象与要存入的对象是否为同一对象。如果true则集合认为添加相同对象,add失败。如果false(不是同一对象)则进行下面的计算。(这一条很重要,保证了,不管如何操作,在HashSet中都不可能存入同一个对象两次)

  • 调用要添加的对象的equals()方法,并把集合中的另一元素作为参数传入,如果返回值为true则集合认为添加相同对象,add失败。否则添加成功。

2、Synchronized底层实现原理

同步方法通过ACC_SYNCHRONIZED 关键字隐式的对方法进行加锁。当线程要执行的方法被标注上ACC_SYNCHRONIZED时,需要先获得锁才能执行该方法。
同步代码块通过monitorenter和monitorexit执行来进行加锁。当线程执行到monitorenter的时候要先获得锁,才能执行后面的方法。当线程执行到monitorexit的时候则要释放锁。每个对象自身维护着一个被加锁次数的计数器,当计数器不为0时,只有获得锁的线程才能再次获得锁。 

3、Synchronized锁的升级过程

Java SE 1.6 为了减少获得锁和释放锁带来的性能消耗,引入了 “偏向锁” 和 “轻量级锁”:锁一共有 4 种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。锁可以升级但不能降级。

偏向锁:大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中记录存储锁偏向的线程ID,以后该线程在进入同步块时先判断对象头的Mark Word里是否存储着指向当前线程的偏向锁,如果存在就直接获取锁。

轻量级锁:当其他线程尝试竞争偏向锁时,锁升级为轻量级锁。线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的MarkWord替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,标识其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

重量级锁:锁在原地循环等待的时候,是会消耗CPU资源的。所以自旋必须要有一定的条件控制,否则如果一个线程执行同步代码块的时间很长,那么等待锁的线程会不断的循环反而会消耗CPU资源。默认情况下锁自旋的次数是10 次,可以使用-XX:PreBlockSpin参数来设置自旋锁等待的次数。10次后如果还没获取锁,则升级为重量级锁。

补充:synchronized锁表现三种形势

Java中每个对象都可以作为锁。具体表现为以下3种方式:

  1. 对于普通方法,锁的是当前实例对象
public class SynchronizedTest {
    public synchronized void test(String name){
        System.out.println(name+"开始执行");
        try {
            Thread.sleep(2000);
        }catch (Exception e){
        }
        System.out.println(name+"执行完毕");
    }
    public static void main(String[] args) throws  Exception {
        SynchronizedTest t=new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.test("线程1");
            }
        }).start();
        SynchronizedTest t1=new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.test("线程2");
            }
        }).start();
    }
}

 上面这个代码我们new了两个不同的对象。打印结果如下。线程2并没有等线程1执行完成后才执行,说明对于普通方法,如果是不同的对象实例锁是不起作用的

线程1开始执行
线程2开始执行
线程2执行完毕
线程1执行完毕

我们把上面的代码修改一下,改为同一个实例

public class SynchronizedTest {
    public synchronized void test(String name){
        System.out.println(name+"开始执行");
        try {
            Thread.sleep(2000);
        }catch (Exception e){
        }
        System.out.println(name+"执行完毕");
    }

    public static void main(String[] args) throws  Exception {
        SynchronizedTest t=new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.test("线程1");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.test("线程2");
            }
        }).start();
    }
}

 打印结果如下。从打印结果可以看出同一个对象实例的时候,第二个线程只有等到第一个线程执行完成后才开始执行。

线程1开始执行
线程1执行完毕
线程2开始执行
线程2执行完毕

  2.对于静态同步方法,锁的是当前类的Class对象。

public class SynchronizedTest {
    public synchronized static void test(String name){
        System.out.println(name+"开始执行");
        try {
            Thread.sleep(2000);
        }catch (Exception e){
        }
        System.out.println(name+"执行完毕");
    }
    public static void main(String[] args) throws  Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                SynchronizedTest.test("线程1");
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                SynchronizedTest.test("线程2");
            }
        }).start();
    }
}

 打印结果如下。第一个线程执行完成后才开始执行第二个线程。

线程1开始执行
线程1执行完毕
线程2开始执行
线程2执行完毕

  3.对于同步方法快,锁的是synchonized括号里配置的对象。

public class SynchronizedTest {
    public  void test(String name){
       Object o=new Object();
        synchronized(o.getClass()){
            System.out.println(name+"开始执行");
            try {
                Thread.sleep(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }

            System.out.println(name+"执行完毕");
        }

    }
    public static void main(String[] args) throws  Exception {
        SynchronizedTest t=new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.test("线程1");
            }
        }).start();
        SynchronizedTest t1=new SynchronizedTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t1.test("线程2");
            }
        }).start();
    }
}

打印结果如下,第一个线程执行完成后才开始执行第二个线程。

线程1开始执行
线程1执行完毕
线程2开始执行
线程2执行完毕

4、spring常用注解

 

 

 

 

5、spring事务的传播机制和隔离级别

spring事务的传播级别:


PROPAGATION_REQUIRED:Spring的默认传播级别,如果上下文中存在事务则加入当前事务,如果不存在事务则新建事务执行。
PROPAGATION_SUPPORTS:如果上下文中存在事务则加入当前事务,如果没有事务则以非事务方式执行。
PROPAGATION_MANDATORY:该传播级别要求上下文中必须存在事务,否则抛出异常。
PROPAGATION_REQUIRES_NEW:该传播级别每次执行都会创建新事务,并同时将上下文中的事务挂起,执行完当前线程后再恢复上下文中事务。(子事务的执行结果不影响父事务的执行和回滚)
PROPAGATION_NOT_SUPPORTED:当上下文中有事务则挂起当前事务,执行完当前逻辑后再恢复上下文事务。(降低事务大小,将非核心的执行逻辑包裹执行。)
PROPAGATION_NEVER:该传播级别要求上下文中不能存在事务,否则抛出异常。
PROPAGATION_NESTED:嵌套事务,如果上下文中存在事务则嵌套执行,如果不存在则新建事务。(save point概念)

事务隔离级别:
 

脏读:读取到了别的事务回滚前的数据,例如B事务修改数据库X,在未提交前A事务读取了X的值,而B事务发生了回滚。
不可重复读:一个事务在两次读取同一个数据的值不一致。例如A事务读取X,在中间过程中B事务修改了X的值,事务A再次读取X时值发生了改变。
幻读:查询得到的数据条数发生了改变,例如A事务搜索数据时有10条数据,在这时B事务插入了一条数据,A事务再搜索时发现数据有11条了。
数据隔离级别
read-uncommitted:未提交读(脏读、不可重复读、幻读)
read-committed:已提交读(不可重复读、幻读),大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。
repeatable-read:可重复读(幻读),保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。
serializable:串行化最严格的级别,事务串行执行,资源消耗最大

Spring事务传播和隔离级别配置


@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class,timeout=1,isolation=Isolation.DEFAULT)

事务的传播性:@Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
事务的超时性:@Transactional(timeout=30) //默认是30秒
事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
回滚指定异常类:@Transactional(rollbackFor={RuntimeException.class,
Exception.class})
只读:@Transactional(readOnly=true)该属性用于设置当前事务是否为只读事务,设置为true表示只读

6、常用的git命令

远程仓库中clone代码到本地:git clone https://github.com/MatchlessHeroVIP/ssmtest.git

新增文件的命令:git add file或者git add .

提交文件的命令:git commit –m或者git commit –a

本地仓库提交到远程仓库:git push

查看工作区状况:git status –s

拉取合并远程分支的操作:git fetch/git merge或者git pull

查看提交记录命令:git reflog

切换到主分支: git checkout master


 

 7、说一说你对IOC、AOP的理解

具体的看前面的IOC、AOP笔记即可

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54558.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Linux打怪升级之路]-秒懂进程地址空间

前言 作者:小蜗牛向前冲 名言:我可以接受失败,但我不能接受放弃 如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 一、C/C下…

【GIT】常用操作总结

一、在本地仓库及远程仓库创建分支,并建立关联关系。 1、创建本地分支,如图: 2、填写分支名称,勾选Checkout branch: 3、提交本地分支到远程分支: 右键选择Git -- Repository -- push: push的时…

Python爬取的网页,需要解码怎么办

前言 本文是该专栏的第26篇,后面会持续分享python的爬虫干货知识,记得关注。 很多时候,在用爬虫采集数据的时候,采集到的源码内容并非我们想要的正确信息,使用正则或者Xpath匹配到的信息也需要我们再次解码才能拿到精准的数据。最近也正是球迷朋友们非常关注卡塔尔世界杯…

[操作系统笔记]基本分段存储管理

内容系听课复习所做笔记,图例多来自课程截图 分段原理 与“分页”最大的区别就是:离散分配时所分配地址空间的基本单位不同 分页是不管程序的逻辑的,规定了页大小就是这么多,但是分段是依据程序自身逻辑来划分的(分页的…

micro-app-源码解析4-数据通信篇-终篇

沙箱篇官方讲解地址 https://github.com/micro-zoe/micro-app/issues/21 1. 带着问题进行阅读 在做框架的时候,没法避免的遇见父子通信和兄弟通信的问题,但是如何实现通信这是个每个框架需要解决的问题。1.1 如何选择父子通信的方式 最好的方式应该是…

IOC操作Bean管理(基于注解方式)

IOC 控制反转,把创建对象过程交给Spring进行管理。 目录 什么是注解 Bean 管理注解方式(创建对象) 基于注解方式实现对象创建 第一步:引入依赖 第二步:开启组件扫描 创建类,在类上面添加创建对象注解…

Python爬虫实战,request+Matplotlib模块,Python绘制一个足球运动员的数据可视化图表

前言 用Python和Matplotlib绘制一个足球运动员的数据可视化图表。 Let’s start happily 开发工具 Python版本: 3.6.4 相关模块: bs4模块 requests模块 pandas模块 matplotlib模块 json模块 numpy模块 环境搭建 安装Python并添加到环境变量…

Sqoop数据导入 第2关:Mysql导入数据至HDFS上

为了完成本关任务,你需要掌握: 1、数据库( MySQL )建表。 2、Mysql 数据导入至 HDFS 中。 数据库( MySQL )建表 用命令进入 MySQL 客户端。 mysql -uroot -p123123 -h127.0.0.1 创建数据库hdfsdb(格式默认为utf8),并在数据库内建立表student,其表结构如下: mys…

通过求解数学模型来选择编码节点的最佳数量和位置(Matlab代码实现)

🍒🍒🍒欢迎关注🌈🌈🌈 📝个人主页:我爱Matlab 👍点赞➕评论➕收藏 养成习惯(一键三连)🌻🌻🌻 🍌希…

【✨十五天搞定电工基础】电阻电路的分析方法

本章要求1. 掌握支路电流法、叠加原理和戴维宁定理等电路的基本分析方法; 2. 了解实际电源的两种模型及其等效变换; 3. 了解非线性电阻元件的伏安特性及静态电阻、动态电阻的概念,以及简单非线性电阻电路 的图解分析法。 目录 一、电阻串并联连接的等效变换 1、…

企业如何有效的防爬虫?

防爬虫,简单来说,就是在尽量减少对正常用户的干扰的情况下尽可能的加大爬虫的成本。 而反防爬虫,或者说制造隐蔽性强的爬虫,就是尽量模拟正常用户的行为。 这两者是动态博弈的。大公司基本都有防爬的项目,以保护数据…

一图了解es6常用数据迭代函数map,filter,fill,reduce

一、一图胜千言 前端开发中,js数组处理是最常用的,除了for循环外,随着es6的普及,像reduce()、filter()、map()、some()、every()以及…展开属性都是最常用到的。 今天偶然从网上看到这张图,真是眼前一亮,真…

盘点JAVA中延时任务的几种实现方式

场景描述 ①需要实现一个定时发布系统通告的功能,如何实现? ②支付超时,订单自动取消,如何实现? 实现方式 一、挂起线程 推荐指数:★★☆ 优点: JDK原生(JUC包下)支持,无需引入新…

RabbitMQ之集群管理

1、在node2、node3、node4三台Linux虚拟机中安装RabbitMQ。 2、从node2拷贝.erlang.cookie到node3、node4的相应目录 如果没有该文件,手动创建/var/lib/rabbitmq/.erlang.cookie ,生成Cookie字符串,或者启动一次RabbitMQ自动生成该文件。生产…

五年Java编程生涯,大专学历最终逆袭阿里,面试+学习+经历分享

五年时间,对于程序员来说,真的非常非常宝贵,掉的头发都够塞满键盘的缝隙了。就说新一代偶像 TFboys 吧,你应该知道这个组合吧,黄金合作期也就三五年的时间,现在基本上就处于各自单飞(solo&#…

深度学习笔记之受限玻尔兹曼机(一)玻尔兹曼分布介绍

机器学习笔记之受限玻尔兹曼机——玻尔兹曼分布介绍引言回顾:Hammersley-Clifford定理玻尔兹曼分布的物理意义引言 从本节开始,将介绍受限玻尔兹曼机。本节将从马尔可夫随机场开始,介绍玻尔兹曼机分布。 回顾:Hammersley-Cliffo…

NCP81239MNTXG 开关降压/升压控制器,USB 功率传递和 Type-C 应用

NCP81239MNTXG USB 功率传递 (PD) 控制器是一款同步降压/升压控制器,适用于为笔记本电脑、平板电脑、台式系统以及很多使用 USB PD 标准和 C−Type 线缆的其他消费设备提供电池电压或适配器电压到所需电源轨的转换。结合使用 USB PD 或 C−Type 接口控制器时完全符合…

【笔记】Java - VM options、Program arguments、Environment variables、eclipse variables

java使用ide开发的时候,运行程序(application)前都要设置启动配置: VM options、Program arguments、Environment variables 很多时候我们是默认配置启动的,所以没太注意。但是如果我们有特殊需求、项目上线时&#xf…

VSCode 安装教程(超详细)

文章目录VSCode 安装使用教程(图文版)那么,什么是 IDE 呢 ?目前,前端开发主流的 IDE 有以下 5 个下边我们开始玩转 VSCodeVSCode 下载、安装Window 版 VScode 安装流程1、下载 Vscode 安装文件2、下载完成单击运行&…

Postman的使用——设置全局参数,参数的传递,从登录接口的响应body中提取数据更新全局参数,从响应cookie中提取数据更新全局变量

Postman的使用——设置全局参数,引用全局参数,参数的传递,从登录接口的响应body中提取数据更新全局参数,从响应cookie中提取数据更新全局变量一、设置全局参数二、引用全局参数三、从登录接口的响应body中提取数据更新全局参数四、…