【面试题】Java面试题--基础总结--个人学习记录

1、Java线程的状态

新建new:新创建一个线程对象。

就绪runnable:创建线程对象后调用start方法,此时线程进入可运行状态,等待CPU的时间片,且其他所需资源已获得。

运行running(一般不考虑这种状态):线程分得CPU的时间片、资源后运行线程。

阻塞blocked:获取锁的时候,锁被其他线程获得,此时该线程进入一个特定的等待队列(同步队列:放的是所有想要获取锁的线程),该队列的线程继续请求空闲锁资源。( IO为最常见的情况,NIO貌似比较特殊,无阻塞????)

等待waiting:调用wait方法,该情况无法自动解除,只能等待通知或中断被唤醒,否则无限期等待,notify,等待队列,且不消耗CPU资源。

超时等待timed-waiting:在一定时间之后自动解除状态,脱离等待队列进入同步队列。

死亡dead:线程执行完毕或意外退出。

补充:锁池、等待池

每个对象都有锁池和等待池,当一个线程A去获取一个对象的锁的时候,如果该锁被其他线程B获取到,则A线程进去该对象的锁池中,并且在该池中有一个同步队列??(关于同步队列和锁池的理解,个人觉得差不多,同步队列偏向于Java思想上的队列维护,锁池是相对于对象而言)如果某一个线程C调用了wait方法,则该线程进入对象的等待池中,此时释放锁资源。如果调用notifyall方法则唤醒所有在等待池中的线程,所有线程进入对象的锁池中,开始新一轮争抢锁资源,如果notify,则随机选择一个进入同步队列。

对象锁与类锁是否冲突:

在Java的jvm里面,方法区存放的是类信息、变量、静态信息等;在栈存放的是方法;堆存放的是所有的实例化对象。

因此在多线程同步方法的时候,如果只有一个实例化对象,那么在调用对象的普通(非静态)方法的时候,需要去竞争对象锁,如果各线程针对的是不同的实例化对象,则不需要竞争。

而类锁是针对于class来说,即有且只有一个class,而实例类会有很多个。类锁即对静态方法进行sychronized加锁。获取的应该是 方法区的类???。

类锁和对象锁针对的不一样,即线程获得类锁后,其他线程仍然可以获得对象锁。

(个人理解:加锁的最终目的是为了线程安全,但是类锁和对象锁针对的对象是分别存放在jvm的方法区和堆中,故不存在线程安全问题,所以二者不会发生突出)

代码示例:

关于使用对象锁的内容,关于使用wait  notify的最明显的是生产者消费者问题,产品没有了,消费者线程进入wait,有产品后,消费者线程进行同步消费,消费时同样需要获得对象锁确保线程安全。(关于类锁、对象锁示例

package com.blog.question;

public class StateOfThreads {
	public synchronized void printfThread(Thread thread) {
		try {
			//获取对象锁后停留几秒钟,但是在执行完这个方法后,即刻释放了该对象锁,但是线程仍然存在,进入了阻塞队列
			System.out.println(thread.getName());
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		StateOfThreads stateOfThreads = new StateOfThreads();
//
//		Thread t1 = new Thread(new Runnable() {
//			@Override
//			public void run() {
//				stateOfThreads.printfThread(Thread.currentThread());
//			}
//		}, "t1");
//		Thread t2 = new Thread(new Runnable() {
//			@Override
//			public void run() {
//				stateOfThreads.printfThread(Thread.currentThread());
//			}
//		}, "t2");
//		t1.start();
//		t2.start();

		 MyThread myThread1 = new MyThread(stateOfThreads);
		 MyThread myThread2 = new MyThread(stateOfThreads);
		 MyThread myThread3 = new MyThread(stateOfThreads);
		 MyThread myThread4 = new MyThread(stateOfThreads);
		 MyThread myThread5 = new MyThread(stateOfThreads);
		
		 myThread1.start();
		 myThread2.start();
		 myThread3.start();
		 myThread4.start();
		 myThread5.start();
	}

}

class MyThread extends Thread {
	private StateOfThreads stateOfThreads;

	public MyThread(StateOfThreads stateOfThreads) {
		this.stateOfThreads = stateOfThreads;
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		super.destroy();
	}

	@Override
	public State getState() {
		// TODO Auto-generated method stub
		return super.getState();
	}

	@Override
	public synchronized void run() {
		stateOfThreads.printfThread(currentThread());
		// System.out.println(stateOfThreads.hashCode());
		// System.out.println(currentThread().hashCode());
		// System.out.println(currentThread().getName()+"获取锁并持有5秒钟");
		System.out.println("释放 锁");
	}

}

图片


2、进程和线程的区别,进程间如何通讯,线程间如何通讯


3、HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别

hashmap数据结构详解(五)之HashMap、HashTable、ConcurrentHashMap 的区别

Java7/8 中的 HashMap 和 ConcurrentHashMap 全解析


4、Cookie和Session的区别(???)

因为http协议是无状态的,无法保存网页浏览时的用户状态。记录一下自己目前的理解。

cookie:以登录为例,在本设备第一次登录远程服务器的时候,远端服务器生成一个对应的cookieID,存放在数据库并返回给客户端,之后用户再次访问的时候,不需要再次登录,而是由浏览器将之前存放的cookie文件取出发送到远端服务器进行cookie认证。

session:session与cookie的最明显区别是一个在服务器端一个在客户端,session大概与cookie原理类似。

(自己目前还没有进行cookie的实验实践,做个记录)


5、索引有什么用?如何建索引?

关于索引:所以最开始的是  线性索引——ISAM——BST树——2-3树——B树——B+树

线性索引:即对原始数据进行划分,在该基础上,建立一个二级索引,或再建立一个三级索引等等,问题一:在插入删除时,所有的二级索引都需要更新,代价很大。问题二:所有的索引都是针对于关键码进行建立的,但是同时也会有很多的辅码存在,当很多记录(且具有相同的辅码)需要进行添加的时候,对这些大量的相同辅码需要浪费很多的额外空间,所以采用一种“倒排表”的方式,即将辅码作为一张表,在相应的辅码后面采用指针将对应的关键码链表连接起来,做到了最原始线性表的一定优化。但是辅码很多的情况下,这种方式在插入一个不同于已有辅码的记录时,需要移动相应的辅码表,代价较大。

ISAM:基于磁盘的索引方法,即在主存中存放了一个柱面(规定的可读磁道),磁盘中存放了很多的柱面(柱面索引、记录、柱面溢出区),访问主存再访问柱面的对应记录,相对来说,两次访问的效率是很好的,但是经过大量的增删之后,柱面溢出区存放了很多新的记录,并且可能柱面溢出区已满,存到了系统溢出区,那么有些记录需要额外访问溢出区和系统溢出区,所以需要定期的重组记录。

BST:树上的记录结点可能存放在不同的磁盘块中,且BST树不平衡,有可能出现,一个增删会将根节点整个一边子树全部重组的情况。

2-3树与B树:2-3树是一个三阶B树,能够保持树高平衡,且不是向下伸展树高,而是通过分裂向上增长树高。

B树与B+树: B+树,在内部结点上面只存储关键码,即内部结点只用作索引确定范围,在叶子结点才用作记录,且所有的叶子结点是存放作为一个链表,且所有的链表是连接在一起的。


6、ArrayList是如何实现的,ArrayList和LinedList的区别?ArrayList如何实现扩容。


7、equals方法实现


8、面向对象

继承、多态、封装


9、线程状态,BLOCKED和WAITING有什么区别

阻塞:线程争抢锁失败,进入同步队列(锁池)。

等待:调用wait方法,需要notify。(消费者生产者问题)


10、JVM如何加载字节码文件

类加载机制

类加载机制02

类加载机制分为了七步:

加载类Loading→验证class文件Verification→ 准备 Preperation→解析class Resolution→ 初始化 Initialization(加载仅5步)

→使用 Using→ 卸载Unloading 

Loading:

1、获取.class文件的二进制流

2、将类信息、静态变量、字节码、常量这些.class文件中的内容放入方法区中

3、在内存中生成一个代表这个.class文件的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。一般这个Class是在堆里的,不过HotSpot虚拟机比较特殊,这个Class对象是放在方法区中的


其他:关于类加载器:

类加载器原理

深入理解Java类加载器(1):Java类加载原理解析

运行时加载类(双亲委派模式)一个类加载器收到类加载请求的时候,先让父类加载器去加载,一直向上,当父类加载器加载失败的时候,才让子类加载器加载,如果全部失败则报错:ClassNotFound。这种错误在基于web开发时,lib文件夹里面没有添加  mysql-connector.jar  的情况下遇到的错误一样(个人理解:多为加载外部导入的jar包是,加载失败导致


Verification:多种方法验证class文件的二进制字节流是否适合当前的虚拟机。(文件格式、元数据、字节码、符号引用)

Preperation:给静态常量分配内存并赋初值,即各种类型变量的零值,具体的复制运算在初始化的时候才会改变高级语言里面的赋值。此时如果静态变量加上了final关键字,则必须在准备阶段进行赋值。

Resolution:解析,将JVM常量池里面的符号引用转化为直接引用(编译原理知识)

Initialization:初始化,类变量、静态变量赋值,执行static代码块。

Using:使用,

Unloading:卸载,


11、JVM GC,GC算法。


12、什么情况会出现Full GC,什么情况会出现yong GC。


13、JVM内存模型

深入理解JVM内存模型

java线程拥有自己的运行内存,另外JVM提供一块主内存,线程工作内存需要与主内存进行load、save类操作。

lock等操作体现在此处。

volitale关键字也是体现在这一部分。当变量被修改后立刻同步刷新到主存。

每个处理器有自己的写缓冲区,而写缓冲区不能做到数据改变试试刷新到主存中,所以引出了指令的重排序。


14、Java运行时数据区

http://www.sohu.com/a/254731966_465221

线程共享:堆(新生代(8:1:1),老年代) 、 方法区

线程私有:JVM栈 、 Native Method stack本地方法栈 、 程序计数器

分别解释:

堆 : 前面提到的关于垃圾回收GC的主要区域,有大量的类存在此处。

方法区:已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

JVM栈:执行java方法,每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

本地方法栈:作用与jvm栈类似,在某些JVM里面,二者为一体。

程序计数器:即一块很小的内存,用于存放下一条指令地址的内存空间。


15、事务的实现原理(????还有问题)

事务:数据库中被视为单一的操作序列。

MySQL事务原理浅析

属性:ACID

  • 原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。

  • 一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。索引约束必须一致。一次事务中要么全部成功,要么全部失败。

  • 隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。封锁协议。

  • 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。保存在硬盘

一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:

  • 使用 begin transaction 命令开始事务。

  • 使用 SQL 查询语句执行各种删除、更新或插入操作。

  • 如果所有的操作都成功,则执行提交操作,否则回滚所有操作。

数据库中的事务管理:数据库主要是两阶段加锁协议等,即数据库内部如何实现的并发控制MVCC(多版本并发控制协议)。

Spring中的事务管理:spring事务主要是在DAO实现层,具体与数据库发生操作的部分,将操作封装为一个事务提交给数据库。


猜你喜欢

转载自blog.csdn.net/whandwho/article/details/82762285