Java面试1

16 篇文章 1 订阅
11 篇文章 1 订阅

Java面试1

一、请你说说线程和协程(进程)的区别

进程和线程的主要差别在于他们是不同的操作系统资源管理方式。
1.进程有【独立的地址空间】,线程有自己的堆栈和局部变量,但是线程之间没有单独的地址空间
2.进程和线程切换时,需要切换进程和线程的上下文,【进程的上下文切换时间开销】远远大于【线程上下文切换时间】,耗费资源较大,效率要差一些
3.【进程的并发性较低,线程的并发性较高】
4.每个独立的进程有一个程序运行的入口,顺序执行序列和程序的出口,线程不能独立执行
5.系统在运行的时候会为【每个进程分配不同的内存空间】,对线程而言,除CPU外,系统不会为线程分配内存
6.一个【进程奔溃后】,在保护模式下不会对其他进程产生影响,但是一个线程奔溃后整个进程都会死掉,所以多进程比多线程更健壮。
① 进程是一个“执行中的程序”,是系统进行资源分配和调度独立单位;
② 线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其他资源;
③ 与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

二、请你说说多线程

进程是操作系统资源调度的基本单位,线程是处理器任务调度和执行的基本单位,一个进程可以创建多个线程,每个线程有自己独立的程序计数器,本地方法栈和虚拟机栈,线程之间共享进程的堆和方法区。线程之间是通过时间片算法来争夺CPU的执行权的。
多线程的好处:当一个线程进入阻塞或者等待状态时,其他的线程可以获取CPU的执行权,提高了CPU的利用率。
多线程的缺点:可能产生死锁;频繁的上下文切换可能会造成资源的浪费;在并发编程中如果因为资源的限制,多线程串行执行,可能速度会比单线程更慢。

三、说说怎么保证线程安全

造成线程安全的原因有三点:原子性,可见性,以及有序性,那么保证线程安全就从这三个方面考虑。 原子性:一个或者多个操作在CPU执行的过程中不被中断的特性。线程切换可能造成原子性问题。 可见性:一个线程对共享变量进行修改,另一个线程能够实时的看见。缓存导致可见性问题。 有序性:程序执行的顺序按照代码的顺序执行。编译优化可能造成这个问题。
解决这些问题: Synchronized关键字可以解决原子性、有序性和可见性问题,同时lock和Atomic开头的类也可以解决原子性问题,volatile关键字可以解决可见性问题和有序性问题。通过禁止指令重排来解决有序性问题。 同时解决有序性问题的处理方案两个关键字的处理方式也不相同。

四、说说线程的状态

1)新建态:当一个线程被创建成功后,但并没有执行它的 start() 方法时处于该状态。
2)就绪态:一个线程执行了 start() 方法进入就绪状态,开始竞争CPU调度权但还没有竞争到以完成它的任务。
3)运行态:一个线程对象获取到了CPU资源调度权,并进入允许态开始完成它的任务。
4)阻塞态:若一个运行中的线程存在同步操作,此时锁被其他线程占用,该线程就会进入阻塞态等待获取锁。
5)限期等待:真正运行的线程执行了 Thread.sleep() 方法或者设置了 timeout的wait() 方法,join() 方法等进入一定时间的等待,系统会自动唤醒。
6)不限期等待:正在运行的线程执行了未设置 timeout 的wait() 方法或者 join() 方法进入等待,只有通过其他线程使用interrupt() 或者 notify() 方法对其进行唤醒。
7)死亡态:线程成功执行完毕或执行中抛出异常中断了,线程就会进入死亡态。

五、请你说说进程间的通信方式

1、管道:管道实质是内核中的一块内存缓存区。创建缓存后会返回两个文件描述符,分别是读与写。有名管道可以在不存在亲缘关系的进程中通信。管道的缺点:只能FIFO、数据无格式且大小受限、并且通信方式是单向的,若需要双向则需要建立两个通道。
2、消息队列:消息队列是内核中的消息链,消息队列支持消息的随机查询,且能够实现多个进程间的读写操作。消息队列相比管道能承载更多的消息。但是消息队列与管道一样,都存在内核与用户内存间的数据拷贝,因此不适合进行频繁的通信。
3、共享内存:共享内存是通过两个进程的虚拟内存中的一块地址映射到同一块物理内存上,共享内存不在需要用户到内核的数据拷贝,通信双方直接操作用户空间的共享内存即可完成通信。
4、信号量:为了解决多进程对共享内存访问时的并发问题,设计了信号量以保证多进程并发访问共享变量的安全。信号量实则是一个计数器,拥有原子操作P和V。当信号量值小于等于0之后再进行P操作会把对应线程或进程阻塞。
5、信号:是事件发生对进程的通知机制,它是软件层次上对中断机制的一种模式,是一种异步通信方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

六、说说线程的创建方式

线程有三种创建方式,分别是:
1、继承 Thread 类,重写 run() 方法,创建该实现类的对象,通过对象调用 start() 方法,无返回值。
2、实现 Runnable 接口,并实现该接口的 run() 方法(推荐使用),创建该实现类的对象传入到 thread 对象中,通过 thread 对象来调用 start() 方法,无返回值 。
3、实现 Callable 接口,重写 call() 方法,创建该实现类的对象创建 futuretask 对象,利用该对象作为参数,创建 thread 对象调用方法,有返回值。

七、说说你了解的线程通信方式

在 Java 中提供了两种多线程通信方式分别是利用 monitor 实现通信方式和使用 condition 实现线程通信方式。
使用不同的线程同步方式也就相应的使用不同的线程通信方式。当我们使用 synchronize 同步时就会使用 monitor 来实现线程通信,这里的 monitor 其实就是锁对象,其利用 object 的 wait(),notify(),notifyAll() 等方法来实现线程通信。
而使用 Lock 进行同步时就是使用 Condition 来实现线程通信,Condition 对象通过 Lock 创建出来依赖于 Lock 对象,使用其 await(),sign()或signAll()方法实现线程通信。

八、说说你对线程池的理解

线程池可以有效的管理线程:
1)它可以管理线程的数量,可以避免无节制的创建线程,导致超出系统负荷直至奔溃。
2)它还可以让线程复用,可以大大地减少创建和销毁线程所带来的开销。线程池需要依赖一些参数来控制任务的执行流程,其中最重要的参数有:corePoolSize(核心线程池数)、workQueue(等待队列)、maxinumPoolSize(最大线程池数)、handler(拒绝策略)、keepAliveTime(空闲线程存活时间)。
当我们想线程池提交一个任务之后,线程池按照如下步骤处理这个任务:
1)判断线程数是否达到 corePoolSize,若没有则新建线程执行该任务,否则进入下一步。
2)判断等待队列是否已满,若没有则将任务放入等待队列,否则进入下一步。
3)判断线程数是否达到 maxinumPoolSize,如果没有则新建线程执行任务,否则进入下一步。
4)采用初始化线程池时指定的拒绝策略,拒绝执行该任务。
5)新建的线程处理完当前任务后,不会立即关闭,而是继续处理等待队列中的任务。如果线程的空闲时间达到了keepAliveTime,则线程池会销毁一部分线程,将线程数量收缩至corePoolSize。第2步中的列队可以有界也可以无界。若指定无界的队列,则线程池永远无法进入第3步,相当于废弃了maxinumPoolSize参数。这种用法是十分危险的,如果任务在队列中产生大量的堆积,就很容易造成内存泄露。JDK为我们提供了一个名为Executors的线程池的创建工具,该工具创建出来的就是带有无界队列的线程池,所以一般在工作中我们是不建议使用这个类来创建线程池的。
6、socket 套接字:可以实现不同主机之间的进程通信。

九、说说你了解的线程同步方式

1、Java通过加锁实现线程同步,锁有两类:synchronized 和 Lock。
2、synchronized 加在三个不同的位置,对应三种不同的使用方式,这三种方式的区别是锁对象不同:
(1)加在普通方法上,则锁是当前的实例(this)。
(2)加在静态方法上,锁是当前类的Class对象。
(3)加在代码块上,则需要在关键字后面的小括号里,显式指定一个对象作为锁对象。
3、Lock支持的功能包括:支持响应中断、支持超时机制、支持以非阻塞的方式获取锁、支持多个条件变量(阻塞队列)。

十、你知道哪些线程安全的集合?

java.util包下的集合大部分都是非线程安全的,如实现List接口的ArrayList,LinkedList,实现set接口的HashSet类,实现SortSet接口的TreeSet类。但是也有极少数的线程安全的集合,如Vector,HashTable等,但是它们是基于Synchronized关键字实现的,性能很差,在开发中不常用。一般可以使用collections工具类中方法将非线程安全的集合类包装成线程安全的类。同时java5之后,concurrent包中提供了大量的支持并发访问的集合类。例如ConcurrentHashMap和CopyOnWriteArrayList等。

十一、请你说说死锁定义及发生的条件

什么是死锁: 两个或两个以上的进程因为争夺资源造成相互等待的现象,若无外力推动,他们将无法执行下去,从而导致死锁的产生
死锁产生的四个条件:
请求与保持条件:当前进程持有至少一个资源,且又去请求其他资源,请求的资源还被占用,进程对于自己占用的资源还不释放
互斥条件:指进程对于自己占有的资源具有排他性,在一段时间内某资源只能有一个进程占用,其他进程想占用只能等待
不剥夺条件:对于进程所持有的资源,无论占用时间在长,也不会剥夺他的占用权
环路等待条件:死锁发生时,必定发生了环路等待,也就是环路集合:{p1、p2、p3…pn}也就是p1等待p2释放资源,p2等待p3释放资源、p3等待pn释放资源以及pn等待p1释放资源。

十二、请你说说乐观锁和悲观锁

乐观锁:乐观锁总是假设最好的情况,每次去拿数据的时候默认别人不会修改,所以不会上锁,只有当更新的时候会判断一下在此期间有没有人更新了这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
悲观锁:悲观锁总是假设最坏的情况,每次去拿数据是都认为别人会修改,所以每次在拿数据时都会上锁,这样别人想拿这个数据时会阻塞直到拿到锁。mysql数据库的共享锁和排他锁都是悲观锁的实现。

十三、说说Java中常用的锁及原理

Java中加锁有两种方式,分别是synchronized关键字和lock锁接口:
synchronized关键字底层采用java对象头来存储锁信息的。
lock锁接口是基于AQS实现的。AQS内部定义一个先进先出的队列实现锁的同步,同时还定义了同步状态来记录锁信息。

十四、请你说说虚拟内存和物理内存的区别

如果没有虚拟内存,那么程序每次寻址都要分配完成的内存空间,而没有分配到内存的进程就需要等待。本次进程执行完成后,再将等待的进程装入内存,效率极低。而且由于进程直接访问物理内存,所以可以修改其他进程的数据,甚至是内核中的数据,不安全。虚拟内存的出现就是为了解决物理内存的种种问题。进程以为自己有连续可用的内存空间,实际是被分割的物理内存碎片,还有部分在暂时存储到外部存储空间,在需要时进行数据交换。

十五、请你说说内存管理

内存管理主要包括内存的分配(malloc)和释放(free),内存的分配可以分为连续内存分配和非连续内存分配。连续分配:为用户分配一个连续的内存空间,常见的连续分配如块式管理。非连续分配又分为页式存储管理,段式存储管理和段页式存储管理。
页式存储管理:页是数据分配的物理单位,页分配是为了实现数据的离散分布,提高内存利用率。它的分区大小是固定的,页相较于块有着更小的分配粒度,可能会导致内部内存碎片,不会导致外部内存碎片。通过页表实现物理地址与逻辑地址相对应。
段式存储管理:段是数据分配的逻辑单位,段式分配目的是为了更好的反映程序的逻辑结构以更好满足用户需求。它的分区大小是不固定的,会导致外部内存碎片,段相比页拥有实际意义,可以将其分为主程序段,子程序段,数据段和栈段。段是一个二维结构,定位段需要知道它的段名和段内具体物理地址。使用段表来对应物理地址和逻辑地址。
段页式存储分配:结合段式分配和页式分配,将主内存区域先分为多个段,在将每个页划分多若干页。结合多段式管理和页式管理的各种优点。

十六、请你说说MySQL索引,以及它们的好处和坏处

MySQL索引是一种帮助快速查找数据的数据结构,可以把它理解为书的目录,通过索引能够快速找到数据所在位置。场景的索引数据结构有:Hash表(通过hash算法快速定位数据,但不适合范围查询,因为需要每个key都进行一次hash)、二叉树(查找和修改效率都比较高),但是在InnoDB引擎中使用的索引是B+Tree,相较于二叉树,B+Tree这种多叉树,更加矮宽,更适合存储在磁盘中。使用索引增加了数据查找的效率,但是相对的由于索引也需要存储到磁盘,所以增加了存储的压力,并且新增数据时需要同步维护索引。但是合理的使用索引能够极大提高我们的效率!
从物理存储角度:
聚集索引(clustered index)、非聚集索引(non-clustered index)
(1) 聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致,聚集索引表记录的排列顺序与索引的排列顺序一致,优点是查询速度快,因为一旦具有第一个索引值的纪录被找到,具有连续索引值的记录也一定物理的紧跟其后。
(2) 聚集索引的缺点是对表进行修改速度较慢,这是为了保持表中的记录的物理顺序与索引的顺序一致,而把记录插入到数据页的相应位置,必须在数据页中进行数据重排,降低了执行速度。非聚集索引指定了表中记录的逻辑顺序,但记录的物理顺序和索引的顺序不一致,聚集索引和非聚集索引都采用了B+树的结构,但非聚集索引的叶子层并不与实际的。
从逻辑角度:
(1) 主键索引:主键索引是一种特殊的唯一索引,不允许有空值
(2) 普通索引或者单列索引
(3) 多列索引(复合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用复合索引时遵循最左前缀集合
(4) 唯一索引或者非唯一索引
(5) 空间索引:空间索引是对空间数据类型的字段建立的索引,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。

十七、请你说说MySQL的事务隔离级别

事务隔离级别是为了解决脏读、不可重复读、幻读。
脏读:一个事务读取了另一个事务未提交的数据。
不可重复读:事务A两次读取的数据不一致,读第二次之前可能有其他事务修改了这个数据并提交了。
幻读:事务A两次读取数据库,两次查询结果的条数不同,称为幻读。行数变了即为幻读,数据变了即为不可重复度。
事务隔离级别如下:
读未提交:以上三个问题都解决不了。
读已提交:只能解决脏读。
可重复读:mysql的默认隔离级别,能解决脏读和不可重复读,包含了间隙锁,可以防止幻读。
串行化:都可以解决。(为每个读取操作加一个共享锁)。

十八、请你说说聚簇索引和非聚簇索引

聚簇索引和非聚簇索引最大的区别是数据域是否和索引项是否分离。聚簇索引一般采用数据表中的具有唯一性的字段(primary,unique,rowid)作为索引,采用b+树结构,在叶子节点存储具体的数据,所以其找到的索引也就找到了具体的数据。而非聚簇索引数据域和索引项是分离的,它的b+树叶子节点存储的是其数据对应的主键值,获取到该主键值后再回表进入聚簇索引中查找具体的数据。一个表中只能有一个聚簇索引,但是可以有多个非聚簇索引。

十九、请你说说索引怎么实现的B+树,为什么选这个数据结构?

索引本质上就是通过预排序+树形结构来加快检索效率的,这个树形结构采用的是多叉树,在非叶子节点上,只存储对应的索引信息,可以简单理解成书的目录,正是由于多叉树的结构,使得大部分是情况下树高都能被控制在2-4层,也就说最多只需要进行4次IO就能够得到数据,让检索效率得到很好的提升。
索引还分为聚簇索引和非聚簇索引,聚簇索引一般用主键来充当,它的叶子节点保存了完整的一行记录,而非聚簇索引一般只存储非聚簇索引和其记录对应的聚簇索引,当通过非聚簇索引找到相应数据的时候,还需要通过叶子节点中的聚簇索引到完整记录表中拿到完整的数据。

二十、数据库为什么不用红黑树而用B+树?

其实最核心的原因就是效率,红黑树它是一个近似平衡的二叉树,它的树高最高都不会超过2*log(n),所以它的性能相对稳定,但是它本质上还是一个二叉树,当数据量多的时候,会增加树高,也就增加了磁盘和硬盘的IO次数。 而B+树是多叉树,所以它能够有效的降低磁盘和硬盘的IO次数,并且它的非叶子节点已经不存放数据了,数据全部放在了叶子结点中,并且已经排序好了,这也符合我们日常对于范围查找和排序的一个要求,所以综合来讲,B+树更适合。

二十一、请你讲讲B树和B+树

B树和B+树都是多路平衡查找树。B树中所有节点都存放数据。B+树只有叶子结点存放数据,其他节点存放key。B树中的叶子结点是独立的,B+书中的叶子结点通过链与相邻叶子结点连接。B树查找使用的是二分查找,没有查找到叶子结点就可能结束,而B+树必须从根节点进行查找,查询效率更稳定。

二十二、请你说说数据库引擎有哪些,各自有什么区别?

1.InnoDB 引擎是 MySQL 的事务安全存储引擎,具备提交、回滚和崩溃恢复功能,支持行锁(通过给索引项加锁来实现),增删改性能更优;
2.MyISAM 引擎支持全文索引,只支持表级锁,通常用于只读或以读为主的场景,表占用空间较小;
3.Memory 引擎是将所有数据都存储在 RAM 中,以便在需要快速查找非关键数据的环境中进行快速访问,以前被称为 HEAP 引擎。
4.Archive 引擎非常适合存储大量的独立的历史数据,拥有高效的插入速度,查询支持较差。

二十三、MySQL主从同步是如何实现的?

复制(replication)是 MySQL 数据库提供的一种高可用高性能的解决方案,一般用来建立大型的应用。总体来说,replication的工作原理分为以下3个步骤:
1.主服务器(master)把数据更改记录到二进制日志(binlog)中。
2.从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中。
3.从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。

二十四、请你介绍一下数据库的ACID

原子性:事务中的任何一个SQL语句执行失败,那么整个事务都应该是失败的,已经执行成功的SQL语句也应该回撤,数据库的状态也应该回到事务执行前的状态。
一致性:事务将数据库从一种状态转变为另一种装填,在转换完成后,数据库的完整性约束并没有发生改变。
隔离性:每个事务读写操作的对象需要与其他事务读写操作的对象相互分离,即该事务提交前对其他事务都不可见。
持久性:事务一旦执行成功,那么结果是永久性的,哪怕系统发生错误,数据库也能将数据恢复。

二十五、请你说说数据库的索引是什么结构,为什么不用哈希表

MySQL 中的索引由 B+ 树实现的。哈希表的查询效率的确最高,时间复杂度 O(1),但是它要求将所有数据载入内存,而数据库存储的数据量级可能会非常大,全部载入内存基本是不可能实现的。B+树可以分段加载需要的节点数据,可以再内存资源有限的前提下,极大提供查询效率。

二十六、请你说说InnoDB的MVCC

MVCC 为多版本并发控制,指的就是使用读已提交和可重复读这两种隔离级别的事务在执行普通 select 操作的时候访记录的版本链的过程,这样子可以使不同的事务读写并发操作,从而提高系统性能。 这两个隔离级别的一个很大不同就是生成readview的时机不同,读已提交在每一次进行普通select操作前就会生成一个readview,而可重复读只在第一次进行普通select的时候生成一个readview,数据的可重复读其实就是read view的重复使用。

二十七、请你说说innodb和myisam的区别?

1,innodb支持事务,myisam不支持。
2,innodb支持行级锁(通过在索引上加锁实现),myisam不支持行级锁.
3,innodb使用聚簇索引和非聚簇索引,而myisam只使用非聚簇索引,innodb不支持全文索引(可通过插件使其支持),myisam支持全文索引。
4,innodb支持外键约束,myisam不支持。
5,innodb最少需要一个文件便可以存储表信息。myisam至少需要三个文件(分别存储表结构,数据,索引)。innodb更加占用空间,myisam相比占据较少。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值