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、进程和线程的区别,进程间如何通讯,线程间如何通讯
– 进程是操作系统的资源调度实体,有自己的内存地址空间和运行环境; – 线程一般被称为轻量级的进程,线程和进程一样,也有自己的运行环境,但是创建一个线程要需要的资源比创建一个进程要少。线程存在于进程之中——每个进程至少有一个线程。一个进程下的多个线程之间可以共享进程的资源,包括内存空间和打开的文件。 – 进程跟程序(programs)、应用(applications)具备相同的含义,进程间通讯依靠IPC资源,例如管道(pipes)、套接字(sockets)等; – 线程间通讯依靠JVM提供的API,例如wait方法、notify方法和notifyAll方法,线程间还可以通过共享的主内存来进行值的传递;
对操作系统来说,线程是最小的执行单元,进程是最小的资源管理单元。
进程 : 一个程序实例,有自己的进程控制块与用户地址空间 , 而进程 : 系统资源调度的最小单元,一个进程里面存在一个或者多个的线程,线程共享 PCB 与 用户地址空间,但是每一个线程用户自己的线程控制块与栈、pc、寄存器等。
进程之间通信使用 : 通道、信号、信号量(进程 切换)、socket
线程通信 : 共享内存机制 与 消息传递机制(使用 pipeInputStream 输入输出流对接,线程之间相互通信,等待输出的线程会一直阻塞住等待另一个线程的输入 --- 有点类似于 wait)
协程 : 一个比较抽象的概念,是比线程更加轻量级的东西,比如使用while循环,知道达到某一个条件之后,才可以继续往后进行,而线程需要wait或者park掉,这样,协程的开销就小了很多,但是cpu 的资源浪费也在。
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如何加载字节码文件
类加载机制分为了七步:
加载类Loading→验证class文件Verification→ 准备 Preperation→解析class Resolution→ 初始化 Initialization(加载仅5步)
→使用 Using→ 卸载Unloading
Loading:
1、获取.class文件的二进制流
2、将类信息、静态变量、字节码、常量这些.class文件中的内容放入方法区中
3、在内存中生成一个代表这个.class文件的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。一般这个Class是在堆里的,不过HotSpot虚拟机比较特殊,这个Class对象是放在方法区中的
其他:关于类加载器:
运行时加载类(双亲委派模式)一个类加载器收到类加载请求的时候,先让父类加载器去加载,一直向上,当父类加载器加载失败的时候,才让子类加载器加载,如果全部失败则报错: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内存模型
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、事务的实现原理(????还有问题)
事务:数据库中被视为单一的操作序列。
属性:ACID
-
原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
-
一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。索引约束必须一致。状态转变后的一致,例如索引前后相同,在MySQL里面通过undo log实现,undo log 日志回滚操作。一致性。举例:针对唯一索引插入相同的值,会失败。
-
隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。封锁协议。
- 持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。保存在硬盘
一个真正的 RDBMS 数据库系统将为每个事务保证所有的四个属性。使用 SQL 发布到数据库中的事务的简单视图如下:
-
使用 begin transaction 命令开始事务。
-
使用 SQL 查询语句执行各种删除、更新或插入操作。
- 如果所有的操作都成功,则执行提交操作,否则回滚所有操作。
数据库中的事务管理:数据库主要是两阶段加锁协议等,即数据库内部如何实现的并发控制MVCC(多版本并发控制协议)。
Spring中的事务管理:spring事务主要是在DAO实现层,具体与数据库发生操作的部分,将操作封装为一个事务提交给数据库。