留学生大佬面经
作者:bobbyparadise
链接:https://www.nowcoder.com/discuss/84899?type=0&order=4&pos=3&page=1
来源:牛客网
拼多多面试:
1,· CHROME给每个tab开了进程,为什么? 其实就是问线程与进程区别 :
· · 进程是具有一定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独立运行的一段程序。
· 线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。在运行时,只是暂用一些计数器、寄存器和栈 。
· 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
· 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
· 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
1 线程与线程间通信
1.1 基本概念以及线程与进程之间的区别联系
· 关于进程和线程,首先从定义上理解就有所不同:
1. 进程是具有一定独立功能的程序、它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独立运行的一段程序。
2. 线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。在运行时,只是暂用一些计数器、寄存器和栈 。
·
他们之间的关系
·
1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)。
2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
3. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
4. 处理机分给线程,即真正在处理机上运行的是线程。
5. 线程是指进程内的一个执行单元,也是进程内的可调度实体。
·
从三个角度来剖析二者之间的区别
·
1. 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
2. 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
3. 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
1.2 多线程间通信方式
· 共享变量
· wait/notify机制
· Lock/Condition机制
· 管道
1.2.1 共享变量
· 线程间发送信号的一个简单方式是在共享对象的变量里设置信号值。线程A在一个同步块里设置boolean型成员变量hasDataToProcess为true,线程B也在同步块里读取hasDataToProcess这个成员变量。这个简单的例子使用了一个持有信号的对象,并提供了set和check方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class MySignal{ protected boolean hasDataToProcess = false; public synchronized boolean hasDataToProcess(){ return this.hasDataToProcess; } public synchronized void setHasDataToProcess(boolean hasData){ this.hasDataToProcess = hasData; } } |
· 线程A和B必须获得指向一个MySignal共享实例的引用,以便进行通信。如果它们持有的引用指向不同的MySingal实例,那么彼此将不能检测到对方的信号。需要处理的数据可以存放在一个共享缓存区里,它和MySignal实例是分开存放的。
1.2.2 wait()/notify机制
· 为了实现线程通信,我们可以使用Object类提供的wait()、notify()、notifyAll()三个方法。调用wait()方法会释放对该同步监视器的锁定。这三个方法必须由同步监视器对象来调用,这可分成两种情况:
o 对于使用synchronized修饰的同步方法,因为该类的默认实例是(this)就是同步监视器,所以可以直接调用这三使用个方法。
o 对于synchronized修饰的同步代码块,同步监视器是synchronized括号里的对象,所以必须使用该对象调用这三个方法。
·
假设系统中有两条线程,这两条线程分别代表取钱者和存钱者。现在系统有一种特殊的要求,系统要求存款者和取钱者不断的实现存款和取钱动作,而且要求每当存款者将钱存入指定账户后,取钱者立即将钱取走.不允许存款者两次存钱,也不允许取钱者两次取钱。
我们通过设置一个旗标来标识账户中是否已有存款,有就为true,没有就标为false。具体代码如下:
·
·
首先我们定义一个Account类,这个类中有取钱和存钱的两个方法,由于这两个方法可能需要并发的执行取钱、存钱操作,所有将这两个方法都修改为同步方法.(使用synchronized关键字)。
·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
public class Account { private String accountNo; private double balance; //标识账户中是否有存款的旗标 private boolean flag=false; public Account() { super(); } public Account(String accountNo, double balance) { super(); this.accountNo = accountNo; this.balance = balance; } public synchronized void draw (double drawAmount){ try { if(!flag){ this.wait(); }else { //取钱 System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount); balance=balance-drawAmount; System.out.println("余额 : "+balance); //将标识账户是否已有存款的标志设为false flag=false; //唤醒其它线程 this.notifyAll(); } } catch (Exception e) { e.printStackTrace(); } } public synchronized void deposit(double depositAmount){ try { if(flag){ this.wait(); } else{ System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount); balance=balance+depositAmount; System.out.println("账户余额为:"+balance); flag=true; //唤醒其它线程 this.notifyAll(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } |
· 接下来创建两个线程类,分别为取钱和存钱线程!
· 取钱线程类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class DrawThread implements Runnable { private Account account; private double drawAmount; public DrawThread(Account account, double drawAmount) { super(); this.account = account; this.drawAmount = drawAmount; } public void run() { for(int i=0;i<100;i++){ account.draw(drawAmount); } } } |
· 存钱线程类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class depositThread implements Runnable{ private Account account; private double depositAmount; public depositThread(Account account, double depositAmount) { super(); this.account = account; this.depositAmount = depositAmount; } public void run() { for(int i=0;i<100;i++){ account.deposit(depositAmount); } } } |
· 最后我们测试一下这个取钱和存钱的操作
1 2 3 4 5 6 7 8 9 10 |
public class TestDraw { public static void main(String[] args) { //创建一个账户 Account account=new Account(); new Thread(new DrawThread(account, 800),"取钱者").start(); new Thread(new depositThread(account, 800),"存款者甲").start(); new Thread(new depositThread(account, 800),"存款者乙").start(); new Thread(new depositThread(account, 800),"存款者丙").start(); } } |
· 大致的输出结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
存款者甲存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者丙存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者甲存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者丙存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者甲存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者丙存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者甲存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者丙存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 存款者甲存钱800.0 账户余额为:800.0 取钱者 取钱:800.0 余额 : 0.0 |
1.2.3 Lock/Condition机制
· 如何程序不使用synchronized关键字来保持同步,而是直接适用Lock对像来保持同步,则系统中不存在隐式的同步监视器对象,也就不能使用wait()、notify()、notifyAll()来协调线程的运行.
· 当使用LOCK对象保持同步时,Java为我们提供了Condition类来协调线程的运行。关于Condition类,JDK文档里进行了详细的解释.,再次就不啰嗦了。
· 我们就拿Account类进行稍微的修改 一下吧!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Account { //显示定义Lock对象 private final Lock lock=new ReentrantLock(); //获得指定Lock对象对应的条件变量 private final Condition con=lock.newCondition(); private String accountNo; private double balance; //标识账户中是否有存款的旗标 private boolean flag=false; public Account() { super(); } public Account(String accountNo, double balance) { super(); this.accountNo = accountNo; this.balance = balance; } public void draw (double drawAmount){ //加锁 lock.lock(); try { if(!flag){ // this.wait(); con.await(); }else { //取钱 System.out.println(Thread.currentThread().getName()+" 取钱:"+drawAmount); balance=balance-drawAmount; System.out.println("余额 : "+balance); //将标识账户是否已有存款的标志设为false flag=false; //唤醒其它线程 // this.notifyAll(); con.signalAll(); } } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock(); } } public void deposit(double depositAmount){ //加锁 lock.lock(); try { if(flag){ // this.wait(); con.await(); } else{ System.out.println(Thread.currentThread().getName()+"存钱"+depositAmount); balance=balance+depositAmount; System.out.println("账户余额为:"+balance); flag=true; //唤醒其它线程 // this.notifyAll(); con.signalAll(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); }finally{ lock.unlock(); } } } |
· 输出结果和上面是一样的!只不过这里显示的使用Lock对像来充当同步监视器,使用Condition对象来暂停指定线程,唤醒指定线程!
1.2.4 管道
·
管道流是JAVA中线程通讯的常用方式之一,基本流程如下:
·
1.
创建管道输出流PipedOutputStream pos和管道输入流PipedInputStream pis
2.
3.
将pos和pis匹配,pos.connect(pis);
4.
5.
将pos赋给信息输入线程,pis赋给信息获取线程,就可以实现线程间的通讯了
6.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class testPipeConnection { public static void main(String[] args) { /** * 创建管道输出流 */ PipedOutputStream pos = new PipedOutputStream(); /** * 创建管道输入流 */ PipedInputStream pis = new PipedInputStream(); try { /** * 将管道输入流与输出流连接 此过程也可通过重载的构造函数来实现 */ pos.connect(pis); } catch (IOException e) { e.printStackTrace(); } /** * 创建生产者线程 */ Producer p = new Producer(pos); /** * 创建消费者线程 */ Consumer1 c1 = new Consumer1(pis); /** * 启动线程 */ p.start(); c1.start(); } } /** * 生产者线程(与一个管道输入流相关联) * */ class Producer extends Thread { private PipedOutputStream pos; public Producer(PipedOutputStream pos) { this.pos = pos; } public void run() { int i = 0; try { while(true) { this.sleep(3000); pos.write(i); i++; } } catch (Exception e) { e.printStackTrace(); } } } /** * 消费者线程(与一个管道输入流相关联) * */ class Consumer1 extends Thread { private PipedInputStream pis; public Consumer1(PipedInputStream pis) { this.pis = pis; } public void run() { try { while(true) { System.out.println("consumer1:"+pis.read()); } } catch (IOException e) { e.printStackTrace(); } } } |
· 程序启动后,就可以看到producer线程往consumer1线程发送数据
consumer1:0
consumer1:1
consumer1:2
consumer1:3
......
·
管道流虽然使用起来方便,但是也有一些缺点
·
o
管道流只能在两个线程之间传递数据 。线程consumer1和consumer2同时从pis中read数据,当线程producer往管道流中写入一段数据后,每一个时刻只有一个线程能获取到数据,并不是两个线程都能获取到producer发送来的数据,因此一个管道流只能用于两个线程间的通讯。不仅仅是管道流,其他IO方式都是一对一传输。
o
o
管道流只能实现单向发送,如果要两个线程之间互通讯,则需要两个管道流 。可以看到上面的例子中,线程producer通过管道流向线程consumer发送数据,如果线程consumer想给线程producer发送数据,则需要新建另一个管道流pos1和pis1,将pos1赋给consumer1,将pis1赋给producer,具体例子本文不再多说。
o
2 进程与进程间通信
2.1 进程间通信方式
· 管道(Pipe) :管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
· 命名管道(named pipe) :命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
· 信号(Signal) :信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;Linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
· 消息(Message)队列 :消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
· 共享内存 :使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
· 内存映射(mapped memory) :内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
· 信号量(semaphore) :主要作为进程间以及同一进程不同线程之间的同步手段。
· 套接口(Socket) :更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:linux和System V的变种都支持套接字。
CVTE一面
· 个人介绍
· · springMVC拦截器和过滤器的区别
http://blog.csdn.net/chenleixing/article/details/44573495
· · excel导出功能如何实现
http://blog.csdn.net/gaojinshan/article/details/30221729
· · SpringIOC和AOP,动态代理实现方式
· · 自动装配有哪几种方式,自动装配有什么局限性
模式 |
说明 |
Default |
在每个bean中都一个autowire=default的默认配置它的含义是: 采用beans和跟标签中的default-autowire="属性值"一样的设置。
|
On |
不使用自动装配,必须通过ref元素指定依赖,默认设置。
|
ByNname |
根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。例如,在bean定义中将autowire设置为by name,而该bean包含master属性(同时提供setMaster(..)方法),Spring就会查找名为master的bean定义,并用它来装配给master属性。 |
Bytype |
如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常,并指出不能使用byType方式进行自动装配。若没有找到相匹配的bean,则什么事都不发生,属性也不会被设置。如果你不希望这样,那么可以通过设置dependency-check="objects"让Spring抛出异常。
|
Constructor |
与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。 |
Antodetect |
通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
|
注意:
byName 和byType
在使用的过程中必须保证bean能够初始化,否则的话会出现bug
如果有默认的无参数的构造器就不需要多余的配置
如果有带有参数的构造器,那在bean的配置中必须配置器初始化的参数 或者在bean中添加无参数的构造器
基本类型不可以装配,精准度 易混乱bean必须可以获得
· · 数据库引擎,Innodb,MyISAM的区别
MyISAM和InnoDB的区别
1. MySQL默认采用的是MyISAM。
2. MyISAM不支持事务,而InnoDB支持。InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务,自动提交,这样会影响速度,所以最好是把多条SQL语句显示放在begin和commit之间,组成一个事务去提交。
3. InnoDB支持数据行锁定,MyISAM不支持行锁定,只支持锁定整个表。即MyISAM同一个表上的读锁和写锁是互斥的,MyISAM并发读写时如果等待队列中既有读请求又有写请求,默认写请求的优先级高,即使读请求先到,所以MyISAM不适合于有大量查询和修改并存的情况,那样查询进程会长时间阻塞。因为MyISAM是锁表,所以某项读操作比较耗时会使其他写进程饿死。
4. InnoDB支持外键,MyISAM不支持。
5. InnoDB的主键范围更大,最大是MyISAM的2倍。
6. InnoDB不支持全文索引,而MyISAM支持。全文索引是指对char、varchar和text中的每个词(停用词除外)建立倒排序索引。MyISAM的全文索引其实没啥用,因为它不支持中文分词,必须由使用者分词后加入空格再写到数据表里,而且少于4个汉字的词会和停用词一样被忽略掉。
7. MyISAM支持GIS数据,InnoDB不支持。即MyISAM支持以下空间数据对象:Point,Line,Polygon,Surface等。
8. 没有where的count(*)使用MyISAM要比InnoDB快得多。因为MyISAM内置了一个计数器,count(*)时它直接从计数器中读,而InnoDB必须扫描全表。所以在InnoDB上执行count(*)时一般要伴随where,且where中要包含主键以外的索引列。为什么这里特别强调“主键以外”?因为InnoDB中primary index是和raw data存放在一起的,而secondary index则是单独存放,然后有个指针指向primary key。所以只是count(*)的话使用secondary index扫描更快,而primary key则主要在扫描索引同时要返回raw data时的作用较大。
· · 索引实现方式,B+树有什么特点,B树和B+数的区别,B+树的实现方式
· · 索引的最左配原则
· · HashMap和HashSet的区别,HashMap是有序的吗
· · LinkedHashMap,保证什么有序,底层实现。
· · java多线程的方式,FutureTask CallAble介绍,CallAble和Runnable的区别
http://www.importnew.com/17572.html
· · 线程池
· · 类的加载过程,双亲委派机制
上午交叉面,下午已回绝。四面挂,心痛。分享面经,为后面校招攒人品。
一面:(8月1号上午:电话面试:80分32秒)
1.自我介绍?
2.做过哪些项目?项目中遇到哪些难点,你是怎样解决的?单点登录系统说一下?分布式缓存的使用场景?(说好的基础呢,上来就是项目,毫无准备,导致好多东西都记不起来了。面试官还说“那你说一个你记得的项目”,手动无奈。。。)
分布式缓存的典型应用场景可分为以下几类:
1)页面缓存.用来缓存Web页面的内容片段,包括HTML、CSS和图片等,多应用于社交网站等;
2)应用对象缓存.缓存系统作为ORM框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;
3)状态缓存.缓存包括Session会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群;
4)并行处理.通常涉及大量中间计算结果需要共享;
5)事件处理.分布式缓存提供了针对事件流的连续查询(continuous query)处理技术,满足实时性需求;
6)极限事务处理.分布式缓存为事务型应用提供高吞吐率、低延时的解决方案,支持高并发事务请求处理,多应用于铁路、金融服务和电信等领域.
3.你实习的时候JDK用的是那个版本,这个版本有什么新的特性?
http://zcc888.gitee.io/2018/03/20/java/#more
Jdk7的新特性:
1.对集合类的语言支持;(??)
2.自动资源管理;
3.改进的通用实例创建类型推断;(??)
4.数字字面量下划线支持;(√)
5.switch中使用string;(√)
JSR292 的实现增加了一个 InvokeDynamic 的字节码指令来支持动态类型语言,使得在把源代码编译成字节码时并不需要确定方法的签名,即方法参数的类型和返回类型。当运行时执行 InvokeDynamic 指令时,JVM 会通过新的动态链接机制 Method Handles,寻找到真实的方法。
G1
ClassLoader 新增 API
为了防止自定义多线程ClassLoad产生的死锁问题,java.lang.ClassLoader类增加了以下API。
4.G1回收器和其他回收器有什么区别?
并发与并行,部分代 自己就可以,局部复制全局标记整理堆>4G会比较好。
Java 8及G1回收器
Java 8 update 20所引入的一个很棒的优化就是G1回收器中的字符串去重(String deduplication)。由于字符串(包括它们内部的char[]数组)占用了大多数的堆空间,这项新的优化旨在使得G1回收器能识别出堆中那些重复出现的字符串并将它们指向同一个内部的char[]数组,以避免同一个字符串的多份拷贝,那样堆的使用效率会变得很低。你可以使用-XX:+UseStringDeduplication这个JVM参数来试一下这个特性。
Java 8及持久代
Java 8中最大的改变就是持久代的移除,它原本是用来给类元数据,驻留字符串,静态变量来分配空间的。这在以前都是需要开发人员来针对那些会加载大量类的应用来专门进行堆比例的优化及调整。许多年来都是如此,这也正是许多OutOfMemory异常的根源,因此由JVM来接管它真是再好不过了。即便如此,它本身并不会减少开发人员将应用解耦到不同的JVM中的可能性。
每个回收器都有许多不同的开关和选项来进行调优,这可能会增加吞吐量,也可能会减少,这取决于你的应用的具体的行为了。在下一篇文章中我们会深入讲解配置这些算法的关键策略。
5.垃圾回收为什么会停顿?哪些对象可能作为GCRoots?
虚拟机栈中引用的对象 方法区(静态属性,常量引用的对象)本地方法栈中引用的对象。
6.垃圾回收分代收集算法?为什么会有两个Survivor区?new一个对象会保存在哪里?
堆
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
http://blog.csdn.net/antony9118/article/details/51425581
设置两个Survivor区最大的好处就是解决了碎片化
7.Java内存模型?volatile关键字,使用场景?原子性的理解?先行发生原则?
Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存(可以与前面将的处理器的高速缓存类比),线程的工作内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的所有操作(读取、赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量。
· lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
· unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
· read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
· load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
· use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
· assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
· store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
· write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。Java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b,load a。Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:
· 不允许read和load、store和write操作之一单独出现
· 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
· 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
· 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
· 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现
· 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
· 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
· 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。
Volatile有五个使用场景(参考Brian Goetz (brian.goetz@sun.com)的文章):
1.作为状态标志
2.一次性安全发布
3.独立观察
4.volatile bean模式
5.开销较低的读写锁策略
Java内存模型中存在的天然的先行发生关系:
1. 程序次序规则:同一个线程内,按照代码出现的顺序,前面的代码先行于后面的代码,准确的说是控制流顺序,因为要考虑到分支和循环结构。
2. 管程锁定规则:一个unlock操作先行发生于后面(时间上)对同一个锁的lock操作。
3. volatile变量规则:对一个volatile变量的写操作先行发生于后面(时间上)对这个变量的读操作。
4. 线程启动规则:Thread的start( )方法先行发生于这个线程的每一个操作。
5. 线程终止规则:线程的所有操作都先行于此线程的终止检测。可以通过Thread.join( )方法结束、Thread.isAlive( )的返回值等手段检测线程的终止。
6. 线程中断规则:对线程interrupt( )方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupt( )方法检测线程是否中断
7. 对象终结规则:一个对象的初始化完成先行于发生它的finalize()方法的开始。
8. 传递性:如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C。
8.场景题:现在有三个线程,同时start,用什么方法可以保证线程执行的顺序,线程一执行完线程二执行,线程二执行完线程三执行?
package com.wenjie.threadDemo;
public class JionTest {
public static void main(String[] args) {
final Thread t1 = new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
System.out.println("t1");
}
});
final Thread t2 = new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
try {
t1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("t2");
}
});
final Thread t3 = new Thread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
try {
t2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("t3");
}
});
t2.start();
t3.start();
t1.start();
}
}
9.你是怎么理解线程安全的?HashMap是线程安全的么?如果多个线程同时修改HashMap时会发生什么情况?
Failfast
死锁
异常concurrentmodeex
10.ConcurrentHashMap底层原理?每个版本的实现上有什么区别?http://www.cnblogs.com/study-everyday/p/6430462.html#autoid-1-0-0
11.静态代理和动态代理的区别?动态代理是怎么实现的?
12.深拷贝和浅拷贝的区别?http://blog.csdn.net/XIAXIA__/article/details/41652057
13.你做过hadoop的项目,为什么要用hadoop?你们处理的文件是什么格式的?写了几个mapper几个reducer?
Hbase,json
14.zookeeper你们用了几台,用来干什么的?一般情况下都是奇数台,那么如果一台挂了,偶数台如何实现master选举?zookeeper的两种启动模式?http://www.cnblogs.com/raphael5200/category/800894.html
15.TCP首部?TCP为什么是可靠的?三次握手四次挥手?
TCP提供一种面向连接的、可靠的字节流服务。
面向连接:意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。在一个TCP连接中,仅有两方进行彼此通信。广播和多播不能用于TCP。
TCP通过下列方式来提供可靠性:
1、应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。 (将数据截断为合理的长度)
2、当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
(超时重发)
3、当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒 。 (对于收到的请求,给出确认响应) (之所以推迟,可能是要对包做完整校验)
4、TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 (校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时时会重发数据)
5、既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。 (对失序数据进行重新排序,然后才交给应用层)
6、既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。(对于重复数据,能够丢弃重复数据)
7、TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。(TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出)TCP使用的流量控制协议是可变大小的滑动窗口协议。
字节流服务::
两个应用程序通过TCP连接交换8bit字节构成的字节流。TCP不在字节流中插入记录标识符。我们将这称为字节流服务(bytestreamservice)。
TCP对字节流的内容不作任何解释:: TCP对字节流的内容不作任何解释。TCP不知道传输的数据字节流是二进制数据,还是ASCII字符、EBCDIC字符或者其他类型数据。对字节流的解释由TCP连接双方的应用层解释。
17.你平时喜欢做什么?看过哪些书?最近在看什么书?(这里有个段子,我:最近在看《HTTP权威指南》。面试官:什么?HTTP潜规则?我:???)
柯南 东野圭吾
舞蹈 运动电影
二面:(8月2号晚上:视频面试:47分钟)
1.你那边怎么有点黑啊?(我靠,我也想说啊,暑假寝室集中供电,然而我们寝室没有电。发面经也不忘吐槽学校,简直坑啊。。。抱着台灯瑟瑟发抖。。。)
2.现在在哪里实习?实习主要做些什么?
3.说一下Java里面你最感兴趣的一个部分?
并发?
4.熟悉Java的哪些框架?用了Spring的哪些东西?Spring现在最新的版本是多少?
5.讲一下MVC设计模式,有什么框架用到了这个模式?spring
6.场景题:一个Controller调用两个Service,这两Service又都分别调用两个Dao,问其中用到了几个数据库连接池的连接?
7.安全方面:如何防止SQL注入?如何防止用户删除其他用户的信息?表单提交可以跨域么?自己写的接口如何防止被其他人恶意调用?
貌似浏览器也不允许js修改已经存在的header,只能增加自定义header: ajax - Set a request header in JavaScript
防止被恶意调用的话,可以做请求限制,第一次请求返回一个随机数,下次请求把随机数带过来,返回时再返回一个随机数
8.zookeeper偶数选举问题?(感觉很重视zookeeper啊)
9.hadoop为什么要将文件分块并且复制三份,复制两份不行么?这样不浪费空间么?namenode脑裂问题?zookeeper如何管理全局配置文件?你们的Hadoop项目用到了几台机器,每个机器的作用?
10.Servlet是线程安全的么?
11.创建一个对象的详细过程?其中发生了些什么?
12.Java NIO你了解么?讲一讲你最熟悉的部分?
13.你在生活中有没有遇到什么神奇的事情,可以分享一下么?比如洒水车为什么老是在阴天或者快下雨的时候洒水?还有我和我老婆经常吵,阳台的花是早上浇好呢,还是晚上浇好呢?(我靠,这是什么套路)
14.你认为你身边的同学怎么样?如果你正在打游戏,你想让同学帮忙带瓶水,你觉得有多少人会帮你带?(这又是什么套路?让我去送水?)
15.你还有什么想问的?
三面:(HR面,8月4号下午:视频面试:22分钟)
1.7岁一个阶段,说一说每一个阶段对你影响最大的一个人或事?
2.说一下你大学阶段做了些什么?
3.你感觉你前两个面试官怎么样?
4.春招的时候为什么没有去bat实习?
5.你当初准备暑期实习的话,是想学到些什么?现在感觉自己有哪些进步?
6.你还有什么想问的?(当然是万能的培养计划和晋升机制)
四面:(交叉面,8月16号上午:电话面试:30分钟)
1.自我介绍?
2.说一下最能代表你技术水平的项目吧?
3.maven如何进行依赖管理,如何解决依赖冲突?
http://blog.csdn.net/honghailiang888/article/details/53019635
为什么会出现依赖冲突
首先要说明Maven的依赖管理,具体的可以参考这边Maven学习——依赖管理 这篇文章,maven在依赖冲管理中有一下几个原则。
1. 依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成。如果两个相同的依赖包,如果groupId, artifactId, version不同,那么maven也认为这两个是不同的。
2. 依赖会传递,A依赖了B,B依赖了C,那么A的依赖中就会出现B和C。
3. Maven对同一个groupId, artifactId的冲突仲裁,不是以version越大越保留,而是依赖路径越短越优先,然后进行保留。
4. 依赖的scope会影响依赖的影响范围。
当出现了依赖的时候如何快速定位冲突原因
但出现了冲突的时候,比如系统出现了NoSuchMethodError,LinkageError 很有可能是你系统中出现了依赖冲突。出现冲突以后,可以按以下的步骤执行
1.确定出了问题的jar包名称。通常可以在eclipse中查找冲突的类有在哪些依赖包里面出现了。并确定实际要使用的是那个包,冲突的包有哪些。
2.通过mvn dependency:tree > tree.txt导出全部的依赖。
3.在导出的依赖文件中,查找问题相关的jar。确定这些jar是如何被依赖进来的,是直接依赖的还是通过传递依赖引入的。
4.找到相互冲突的并需要排除的依赖的顶级依赖,并分析冲突的原因,冲突的原因可能是以下几种:
· 同一个jar包但groupId, artifactId不同,这种冲突只能通过设定依赖的<exclusions>来进行排除
· 需要的版本jar包依赖路径较长,这种冲突可以把想要版本的依赖直接什么在依赖中,这样路径就最短了优先级最高。
5.最后可以通过打包mvn install来确认打出来的war包中是否有被排除的依赖。
4.maven的源和插件了解哪些?maven的生命周期?
http://blog.csdn.net/limm33/article/details/53142118
5.如何保证分布式缓存的一致性?分布式session实现?
6.spring的bean的创建时机?依赖注入的时机?
7.你们的图片时怎么存储的,对应在数据库中时如何保存图片的信息的?
http://blog.csdn.net/haluoluo211/article/details/52080325最好是二进制或者项目中,直接保存相对路径
8.单点登录系统的实现?
http://www.cnblogs.com/ywlaker/p/6113927.html
9.项目中用到的JDK的哪些特性?
10.java8流式迭代的好处?
http://www.cnblogs.com/shenlanzhizun/p/6027042.html
11.多线程如何在多个CPU上分布?线程调度算法有哪些?
12.线程调度和进程调度的区别?
13.项目中用到了哪些集合类?
14.说一下TreeMap的实现原理?红黑树的性质?红黑树遍历方式有哪些?如果key冲突如何解决?setColor()方法在什么时候用?什么时候会进行旋转和颜色转换?后者覆盖前者 保持唯一性
15.你有什么想问的?
时隔两年,再一次的面临离职找工作,这一次换工作有些许的不舍,也有些许的无奈。个人所在的技术团队不错,两年时间成长了很多,也很不舍这个团队。但是,由于公司的某些原因和对于自身未来发展的综合考虑,又不得不得离去,去寻找更合适的地方成长和发展。相比于两年前,现在找工作没有那么的着急,也没有那么的迫切,也没有特别想去的公司,反正去大厂互联网公司基本都是加班加点的。也许,这是工作三年的我即将面临的一个坎吧。对于未来的选择和考虑,对于未来的恐惧吧。也许我是杞人忧天,也许是上天注定我将去某一家公司,不管怎样,坚持做最好的自己!不停进步,不停成长!
下面将最近面试遇到的一些题目进行汇总如下,希望对现在正在找工作的同学和现在面临毕业找工作的同学有所帮助。
1、常用设计模式
·
单例模式:懒汉式、饿汉式、双重校验锁、静态加载,内部类加载、枚举类加载。保证一个类仅有一个实例,并提供一个访问它的全局访问点。
·
·
代理模式:动态代理和静态代理,什么时候使用动态代理。
·
·
适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
·
·
装饰者模式:动态给类加功能。
·
·
观察者模式:有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
·
·
策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。
·
·
外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
·
·
命令模式:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
·
·
创建者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
·
·
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
·
2、基础知识
·
Java基本类型哪些,所占字节和范围
·
·
Set、List、Map的区别和联系
·
·
什么时候使用HashMap
·
·
什么时候使用LinkedHashMap、ConcurrentHashMap、WeakHashMap
·
·
哪些集合类是线程安全的
·
·
为什么Set、List、Map不实现Cloneable和Serializable接口
·
·
ConcurrentHashMap的实现,1.7和1.8的实现
·
·
Arrays.sort的实现
·
·
什么时候使用CopyOnArrayList
·
·
volatile的使用
·
·
Synchronied的使用
·
·
ReentrantLock的实现以及和Synchronied的区别
·
·
CAS的实现原理以及问题
·
·
AQS的实现原理
·
·
接口和抽象类的区别,什么时候使用
·
·
类加载机制的步骤,每一步做了什么,static和final修改的成员变量的加载时机
·
·
双亲委派模型
·
·
反射机制:反射动态擦除泛型、反射动态调用方法等
·
·
动态绑定:父类引用指向子类对象
·
·
JVM内存管理机制:有哪些区域,每个区域做了什么
·
·
JVM垃圾回收机制:垃圾回收算法 垃圾回收器 垃圾回收策略
·
·
jvm参数的设置和jvm调优
·
·
什么情况产生年轻代内存溢出、什么情况产生年老代内存溢出
·
·
内部类:静态内部类和匿名内部类的使用和区别
·
·
redis和memcached:什么时候选择redis,什么时候选择memcached,内存模型和存储策略是什么样的
·
·
·
·
mysql的优化策略有哪些
·
·
mysql索引的实现B+树的实现原理
·
·
什么情况索引不会命中,会造成全表扫描
·
·
java中bio nio aio的区别和联系
·
·
为什么bio是阻塞的nio是非阻塞的nio的模型是什么样的
·
·
java io的整体架构和使用的设计模式
·
·
Reactor模型和Proactor模型
·
·
http请求报文结构和内容
·
·
http三次握手和四次挥手
·
·
rpc相关:如何设计一个rpc框架,从io模型 传输协议 序列化方式综合考虑
·
·
linux命令 统计,排序,前几问题等
·
·
StringBuff和StringBuilder的实现,底层实现是通过byte数据,外加数组的拷贝来实现的
·
·
cas操作的使用
·
·
内存缓存和数据库的一致性同步实现
·
·
微服务的优缺点
·
·
线程池的参数问题
·
·
ip问题 如何判断ip是否在多个ip段中
·
·
判断数组两个中任意两个数之和是否为给定的值
·
·
乐观锁和悲观锁的实现
·
·
synchronized实现原理
·
·
你在项目中遇到的困难和怎么解决的
·
·
你在项目中完成的比较出色的亮点
·
·
消息队列广播模式和发布/订阅模式的区别
·
·
生产者消费者代码实现
·
·
死锁代码实现
·
·
线程池:参数,每个参数的作用,几种不同线程池的比较,阻塞队列的使用,拒绝策略
·
·
Future和ListenableFuture异步回调相关
·
·
算法相关:判断能否从数组中找出两个数字和为给定值,随机生成1~10000不重复并放入数组,求数组的子数组的最大和,二分查找算法的实现及其时间复杂计算
·
3、其它
·
算法:常用排序算法,二分查找,链表相关,数组相关,字符串相关,树相关等
·
·
常见序列化协议及其优缺点
·
·
memcached内存原理,为什么是基于块的存储
·
·
搭建一个rpc需要准备什么
·
·
如果线上服务器频繁地出现full gc,如何去排查
·
·
如果某一时刻线上机器突然量变得很大,服务扛不住了,怎么解决
·
·
LRU算法的实现
·
·
LinkedHashMap实现LRU
·
·
定义栈的数据结构,请在该类型中实现一个能够找到栈最小元素的min函数
·
·
海量数据处理的解决思路
·
·
reactor模型的演变
·
·
阻塞、非阻塞、同步、异步区别
·
·
Collection的子接口
·
·
jvm调优相关
·
·
zookeeper相关,节点类型,如何实现服务发现和服务注册
·
·
nginx负载均衡相关,让你去实现负载均衡,该怎么实现
·
·
linux命令,awk、cat、sort、cut、grep、uniq、wc、top等
·
·
压力测试相关,怎么分析,单接口压测和多情况下的压测
·
·
你觉得你的优点是什么,你的缺点是什么,为什么我们要录用你
·
·
Spring mvc的实现原理
·
·
netty底层实现,IO模型,ChannelPipeline的实现和原理
·
·
缓存的设计和优化
·
·
缓存和数据库一致性同步解决方案
·
·
你所在项目的系统架构,谈谈整体实现
·
·
消息队列的使用场景
·
·
ActiveMQ、RabbitMQ、Kafka的区别
·
·
JVM内存分配策略和垃圾回收时机
·
·
JVM垃圾回收策略和对象的实际生命周期
·
·
对象是如何在堆中分配的
·
·
full gc触发的场景,线上如果一直full gc怎么排查
·
·
如何防止接口被恶意攻击
·
·
线上某一台服务器出现问题,如何实际操作去解决
·
·
线上接口最大的QPS以及如何评估服务器可承受的最大请求数
·
·
内存缓存的使用场景和优缺点
·
·
怎么防止线上接口被恶意刷
·
·
怎么防止线上接口被攻击
·
·
栈帧的结构
·
·
transient关键字的使用
·
以上内容为近期的面试总结。后续如有新的内容,会持续更新。
hashMap可以key为空或者value为空吗?可以同时为空吗?
HashSet有什么特点?
话题二:
HashMap底层是怎么实现的?
1.6:数组+链表
1.8:数组+链表/红黑树
为什么要二次hash?谈谈扩容|什么时候扩容?
HashMap与Hashtable有什么区别吗?
Hashtable加同步锁有什么问题?能不能有更好的实现方式?
ConcurrentHashMap底层怎么实现的?怎么保证同步的(CAS)?
话题三:
如果前面的,你都答出来了?
什么是CAS?它synchronized有什么区别?
谈谈synchronized?
能谈一下volatile吗?
6、JVM连环炮+并发编程?
话题一:
1、谈谈内存区域?
堆、栈、方法区、(程序计算器、本地方法栈 )|可以不谈
2、堆、栈、方法区分别有什么用?存储什么内容?
3、能谈谈堆吗?
新生代、老年代
4、怎么判断对象不可用?
5、能谈一下GC策略吗?它是怎么回收不可用对象的?
标记-清除,复制、标记-整理、分代。
6、能谈一下垃圾回收器吗?(暂无碰见问的)
7、对象怎么分配的?
话题二:
1、谈一下类加载过程?
2、谈一下双亲委派机制?
话题三:
1、谈一下Java内存模型
2、谈一下Synchronized、volatile?
2、谈一下AQS、CountDownLatch、ReentrantLock?(暂时还没有人问)
3、谈一下ThreadLocal?
4、谈一下CAS?
5、谈一下线程池原理?
二、设计模式
1、谈一下六种设计原则?开闭原则什么意思?里氏XX是什么意思?
2、写一个单例模式?后续自己扩展各种的优缺点?
3、谈一个你熟悉的设计模式?
三、缓存
1、设计一个高效的缓存?
http://www.cnblogs.com/xiaopohou/archive/2011/08/31/2160437.html
2、谈谈LRU算法?(解决高效的缓存)
三、架构思想、高并发、大数据量解决方案。
1、一个很大的日记文件,统计出访问排行?
2、mapreduce思想?谈谈shuffle?
1、unordered_map的实现原理。。很具体的描述
2、单链表多路归并排序
3、LRU cache的实现,插入、查找、删除时间复杂度不能超过O(1)
有一个场景,现在cache满了,需要淘汰频率最低的那个
更新:0824华为
关于HTTP请求GET和POST的区别
1.GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头<request-line>中),以?分割URL和传输数据,多个参数用&连接;例如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0 %E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。
POST提交:把提交的数据放置在是HTTP包的包体<request-body>中。上文示例中红色字体标明的就是实际的传输数据
因此,GET提交的数据会在地址栏中显示出来,而POST提交,地址栏不会改变
2.传输数据的大小:
首先声明,HTTP协议没有对传输的数据大小进行限制,HTTP协议规范也没有对URL长度进行限制。而在实际开发中存在的限制主要有:
GET:特定浏览器和服务器对URL长度有限制,例如IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。
因此对于GET提交时,传输数据就会受到URL长度的限制。
POST:由于不是通过URL传值,理论上数据不受限。但实际各个WEB服务器会规定对post提交数据大小进行限制,Apache、IIS6都有各自的配置。
3.安全性:
POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,
Http协议:一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,下图给出了请求报文的一般格式。
请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
User-Agent:产生请求的浏览器类型。
Accept:客户端可识别的内容类型列表。
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。
HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文。
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
对称密码加解密,非对称密码加解密。
事务隔离级别和实现方式。
反射:固化框架层的,业务层实际上用不太到。JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
遇到问题:分布式加锁问题:
memcached分布式锁:1、实现原理:
memcached带有add函数,利用add函数的特性即可实现分布式锁。add和set的区别在于:如果多线程并发set,则每个set都会成功,但最后存储的值以最后的set的线程为准。而add的话则相反,add会添加第一个到达的值,并返回true,后续的添加则都会返回false。利用该点即可很轻松地实现分布式锁。
2、优点
并发高效。
3、缺点
效率对库依赖比较重,重量级
(1)memcached采用列入LRU置换策略,所以如果内存不够,可能导致缓存中的锁信息丢失。
(2)memcached无法持久化,一旦重启,将导致信息丢失。
Redis:redis分布式锁即可以结合zk分布式锁锁高度安全和memcached并发场景下效率很好的优点,可以利用jedis客户端实现。
URL到页面相应的全过程:
浏览器缓存etc缓存 路由缓存 服务提供商缓存 递归到根dns,请求连接TCP。发送报文,恢复
Spring AOP开发用到了那些部分?
==一般只用到了基本数据类型比较
1、equals方法用于比较对象的内容是否相等(覆盖以后)
2、hashcode方法只有在集合中用到
3、当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。
4、将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
作者:Xoper.ducky
链接:https://www.nowcoder.com/discuss/3043
来源:牛客网
J2SE基础
1. 九种基本数据类型的大小,以及他们的封装类。
基本类型 |
大小(字节) |
默认值 |
封装类 |
byte |
1 |
(byte)0 |
Byte |
short |
2 |
(short)0 |
Short |
int |
4 |
0 |
Integer |
long |
8 |
0L |
Long |
float |
4 |
0.0f |
Float |
double |
8 |
0.0d |
Double |
boolean |
- |
false |
Boolean |
char |
2 |
\u0000(null) |
Character |
void |
- |
- |
Void |
2. Switch能否用string做参数?
3. equals与==的区别。
4. Object有哪些公用方法?
Wait notify notifyall clone equels getclass
5. Java的四种引用,强弱软虚,用到的场景。
6. Hashcode的作用。
7. ArrayList、LinkedList、Vector的区别。
8. String、StringBuffer与StringBuilder的区别。
9. Map、Set、List、Queue、Stack的特点与用法。
10. HashMap和HashTable的区别。
11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
12. TreeMap、HashMap、LindedHashMap的区别。
13. Collection包结构,与Collections的区别。
14. try catch finally,try里有return,finally还执行么?
15. Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。
16. Java面向对象的三个特征与含义。
17. Override和Overload的含义去区别。
18. Interface与abstract类的区别。
19. Static class 与non static class的区别。
20. java多态的实现原理。
同一操作不同结果,前期绑定后期绑定
21.实现多线程的两种方法:Thread与Runable。Call
22.线程同步的方法:sychronized、lock、reentrantLock等。
23.锁的等级:方法锁、对象锁、类锁。
24.写出生产者消费者模式。
25. ThreadLocal的设计理念与作用。Thread ThreadLocal->ThreadLocalMap
26. ThreadPool用法与优势。
27. Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
28. wait()和sleep()的区别。
29. foreach与正常for循环效率对比。
30. Java IO与NIO。
31.反射的作用于原理。
32.泛型常用特点,List<String>能否转为List<Object>。
33.解析XML的几种方式的原理与特点:DOM、SAX、PULL。
34. Java与C++对比。
35. Java1.7与1.8新特性。
36.设计模式:单例、工厂、适配器、责任链、观察者等等。
37. JNI的使用。
Java里有很多很杂的东西,有时候需要你阅读源码,大多数可能书里面讲的不是太清楚,需要你在网上寻找答案。
推荐书籍:《java核心技术卷I》《Thinking in java》《java并发编程》《effictive java》《大话设计模式》
JVM
1.内存模型以及分区,需要详细到每个区放什么。
2.堆里面的分区:Eden,survival from to,老年代,各自的特点。
3.对象创建方法,对象的内存分配,对象的访问定位。
4. GC的两种判定方法:引用计数与引用链。
5. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方,如果让你优化收集方法,有什么思路?
6. GC收集器有哪些?CMS收集器与G1收集器的特点。
7. Minor GC与Full GC分别在什么时候发生?
8.几种常用的内存调试工具:jmap、jstack、jconsole。
9.类加载的五个过程:加载、验证、准备、解析、初始化。
10.双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader。
11.分派:静态分派与动态分派。
JVM过去过来就问了这么些问题,没怎么变,内存模型和GC算法这块问得比较多,可以在网上多找几篇博客来看看。
推荐书籍:《深入理解java虚拟机》
操作系统
1.进程和线程的区别。
2.死锁的必要条件,怎么处理死锁。
3. Window内存管理方式:段存储,页存储,段页存储。
4.进程的几种状态。
5. IPC几种通信方式。
6.什么是虚拟内存。
7.虚拟地址、逻辑地址、线性地址、物理地址的区别。
因为是做android的这一块问得比较少一点,还有可能上我简历上没有写操作系统的原因。
推荐书籍:《深入理解现代操作系统》
TCP/IP
1. OSI与TCP/IP各层的结构与功能,都有哪些协议。
2. TCP与UDP的区别。
3. TCP报文结构。
4. TCP的三次握手与四次挥手过程,各个状态名称与含义,TIMEWAIT的作用。
5. TCP拥塞控制。
6. TCP滑动窗口与回退N针协议。
7. Http的报文结构。
8. Http的状态码含义。
9. Http request的几种类型。
10. Http1.1和Http1.0的区别
11. Http怎么处理长连接。
12. Cookie与Session的作用于原理。
13.电脑上访问一个网页,整个过程是怎么样的:DNS、HTTP、TCP、OSPF、IP、ARP。
14. Ping的整个过程。ICMP报文是什么。
15. C/S模式下使用socket通信,几个关键函数。
16. IP地址分类。
17.路由器与交换机区别。
网络其实大体分为两块,一个TCP协议,一个HTTP协议,只要把这两块以及相关协议搞清楚,一般问题不大。
推荐书籍:《TCP/IP协议族》
数据结构与算法
1.链表与数组。
2.队列和栈,出栈与入栈。
3.链表的删除、插入、反向。
4.字符串操作。
5. Hash表的hash函数,冲突解决方法有哪些。
6.各种排序:冒泡、选择、插入、希尔、归并、快排、堆排、桶排、基数的原理、平均时间复杂度、最坏时间复杂度、空间复杂度、是否稳定。
7.快排的partition函数与归并的Merge函数。
8.对冒泡与快排的改进。
9.二分查找,与变种二分查找。
10.二叉树、B+树、AVL树、红黑树、哈夫曼树。
11.二叉树的前中后续遍历:递归与非递归写法,层序遍历算法。
12.图的BFS与DFS算法,最小生成树prim算法与最短路径Dijkstra算法。
13. KMP算法。
14.排列组合问题。
15.动态规划、贪心算法、分治算法。(一般不会问到)
16.大数据处理:类似10亿条数据找出最大的1000个数.........等等
算法的话其实是个重点,因为最后都是要你写代码,所以算法还是需要花不少时间准备,这里有太多算法题,写不全,我的建议是没事多在OJ上刷刷题(牛客网、leetcode等),剑指offer上的算法要能理解并自己写出来,编程之美也推荐看一看。
推荐书籍:《大话数据结构》《剑指offer》《编程之美》
以下是我常问的几个问题,如果需要可以参考:
1.贵公司一向以XXX著称,能不能说明一下公司这方面的特点?
2.贵公司XXX业务发展很好,这是公司发展的重点么?
3.对技术和业务怎么看?
4.贵公司一般的团队是多大,几个人负责一个产品或者业务?
5.贵公司的开发中是否会使用到一些最新技术?
6.对新人有没有什么培训,会不会安排导师?
7.对Full Stack怎么看?
8.你觉得我有哪些需要提高的地方?
网易小袁兵总结:
1 java concurrent包下有哪些类,了解哪些锁
2 hashmap和concurrenthashmap有什么区别?各自的机制是什么样的?hashmap如何解决链表过长?红黑树有哪些特性?
· * 每个结点要么是“红色”,要么是“黑色”(后面将说明)
· * 所有的叶结点都是空结点,并且是“黑色”的
· * 如果一个结点是“红色”的,那么它的两个子结点都是“黑色”的
· * (注:也就是說,如果結點是黑色的,那么它的子節點可以是紅色或者是黑色的)。
· * 结点到其子孙结点的每条简单路径都包含相同数目的“黑色”结点
· * 根结点永远是“黑色”的
红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。
· 之所以称为红黑树的原因就在于它的每个结点都被“着色”为红色或黑色。这些结点颜色被用来检测树的平衡性。但需要注意的是,红黑树并不是平衡二叉树,恰恰相反,红黑树放松了平衡二叉树的某些要求,由于一定限度的“不平衡”,红黑树的性能得到了提升。
·
· 从根结点到叶结点的黑色结点数被称为树的“黑色高度”(black-height)。前面关于红黑树的性质保证了从根结点到叶结点的路径长度不会超过任何其他路径的两倍。
对于给定的黑色高度为n的红黑树,从根到叶结点的简单路径的最短长度为n-1,最大长度为2(n-1)。
3 怎么样实现公平锁?实现机制?
同步队列啊有头信息获得锁之前看是否有前驱节点没有获得锁。
4 java引用类型有哪些?各有什么特点?
StrongReference
就可以他的名字一样,任何时候GC是不能回收他的,哪怕内存不足时,系统会直接抛出异常OutOfMemoryError,也不会去回收,首先要说明的是java中默认就是强引用,
SoftReference
软引用他的特点是当内存足够时不会回收这种引用类型的对象,只有当内存不够用时才会回收,这种特点很适合最一些缓存。
SoftReference<Person> personSoftReference = new SoftReference<>(jack);
WeakReference
虚引用的特点是只要GC一运行就会把给回收了,个人感觉没多大用处,因为只要GC一运行他就会被回收了。
WeakReference<Person> personSoftReference = new WeakReference<Person>(jack);
5 遇过哪些内存泄漏的问题?什么原因?
指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。就算数据失效了,但是就是不可以gc.
内存泄露是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄露。
1、静态集合类引起内存泄露:
像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。
例:
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//
在这个例子中,循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放引用本身(o=null),那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用。
例:
public static void main(String[] args)
{
Set<Person> set = new HashSet<Person>();
Person p1 = new Person("唐僧","pwd1",25);
Person p2 = new Person("孙悟空","pwd2",26);
Person p3 = new Person("猪八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素!
p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变
set.remove(p3); //此时remove不掉,造成内存泄漏
set.add(p3); //重新添加,居然添加成功
System.out.println("总共有:"+set.size()+"个元素!"); //结果:总共有:4个元素!
for (Person person : set)
{
System.out.println(person);
}
}
3、监听器
在java编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC回收的。对于Resultset和Statement对象可以不进行显式回收,但Connection一定要显式回收,因为Connection在任何时候都无法自动回收,而Connection一旦回收,Resultset和Statement对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
5、内部类和外部模块等的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A负责A模块,调用了B模块的一个方法如:
public void registerMsg(Object b);
这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B是否提供相应的操作去除引用。
6、单例模式
不正确使用单例模式是引起内存泄露的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露,考虑下面的例子:
class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B类采用单例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter...
}
显然B采用singleton模式,它持有一个A对象的引用,而这个A类的对象将不能被回收。想象下如果A是个比较复杂的对象或者集合类型会发生什么情况
6 怎么样实现list的线程安全?
Copyandwritearraylist concurrent...Vector
7 怎么样实现一个缓存中的LRU算法?
yes
8 listhashmap的原理
Java LinkedHashMap工作原理及实现
2016/03/22 |分类:基础技术 | 0条评论 |标签:LinkedHashMap
分享到:11
原文出处:Yikun
1. 概述
在理解了#7介绍的HashMap后,我们来学习LinkedHashMap的工作原理及实现。首先还是类似的,我们写一个简单的LinkedHashMap的程序:
1 2 3 4 5 6 7 8 9 10 11 12 |
LinkedHashMap<String, Integer> lmap = new LinkedHashMap<String, Integer>(); lmap.put("语文", 1); lmap.put("数学", 2); lmap.put("英语", 3); lmap.put("历史", 4); lmap.put("政治", 5); lmap.put("地理", 6); lmap.put("生物", 7); lmap.put("化学", 8); for(Entry<String, Integer> entry : lmap.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } |
运行结果是:
1 2 3 4 5 6 7 8 |
语文: 1 数学: 2 英语: 3 历史: 4 政治: 5 地理: 6 生物: 7 化学: 8 |
我们可以观察到,和HashMap的运行结果不同,LinkedHashMap的迭代输出的结果保持了插入顺序。是什么样的结构使得LinkedHashMap具有如此特性呢?我们还是一样的看看LinkedHashMap的内部结构,对它有一个感性的认识:
没错,正如官方文档所说:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked listrunning through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
LinkedHashMap是Hash表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序。
2. 三个重点实现的函数
在HashMap中提到了下面的定义:
1 2 3 4 |
// Callbacks to allow LinkedHashMap post-actions void afterNodeAccess(Node<K,V> p) { } void afterNodeInsertion(boolean evict) { } void afterNodeRemoval(Node<K,V> p) { } |
LinkedHashMap继承于HashMap,因此也重新实现了这3个函数,顾名思义这三个函数的作用分别是:节点访问后、节点插入后、节点移除后做一些事情。
afterNodeAccess函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
void afterNodeAccess(Node<K,V> e) { // move node to last LinkedHashMap.Entry<K,V> last; // 如果定义了accessOrder,那么就保证最近访问节点放到最后 if (accessOrder && (last = tail) != e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null) head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } } |
就是说在进行put之后就算是对节点的访问了,那么这个时候就会更新链表,把最近访问的放到最后,保证链表。
afterNodeInsertion函数
1 2 3 4 5 6 7 8 |
void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; // 如果定义了移除规则,则执行相应的溢出 if (evict && (first = head) != null && removeEldestEntry(first)) { K key = first.key; removeNode(hash(key), key, null, false, true); } } |
如果用户定义了removeEldestEntry的规则,那么便可以执行相应的移除操作。
afterNodeRemoval函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void afterNodeRemoval(Node<K,V> e) { // unlink // 从链表中移除节点 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; } |
这个函数是在移除节点后调用的,就是将节点从双向链表中删除。
我们从上面3个函数看出来,基本上都是为了保证双向链表中的节点次序或者双向链表容量所做的一些额外的事情,目的就是保持双向链表中节点的顺序要从eldest到youngest。
9 手写一个算法,随便一个整数num,一个代表进制的整数k(小于10),实现num的k进制转换
10 实现一个算法,数据库有一个按顺序排列的歌单,用户在前台修改了顺序,现要把数据库的歌单修改成同样的顺序,问怎么样操作最少.
11 实现一个算法,一个文档有若干单词(内存放不下),现要求出10个频度最高的单词
12 jvm中适合高吞吐量的垃圾回收器
http://blog.csdn.net/lghuntfor/article/details/51052737
(1)特点:
–同ParNew回收器一样, 不同的地方在于,它非常关注系统的吞吐量(通过参数控制)
–使用复制算法
–支持自适应的GC调节策略
(3)设置参数:
-XX:+UseParallelGC新生代用ParallelGC回收器, 老年代使用串行回收器
-XX:+UseParallelOldGC新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系统吞吐量的控制:
-XX:MaxGCPauseMillis=n(单位ms) 设置垃圾回收的最大停顿时间,
-XX:GCTimeRatio=n(n在0-100之间) 设置吞吐量的大小, 假设值为n, 那系统将花费不超过1/(n+1)的时间用于垃圾回收
-XX:+UseAdaptiveSizePolicy打开自适应GC策略, 在这种模式下, 新生代的大小, eden,survivior的比例, 晋升老年代的对象年龄等参数会被自动调整,以达到堆大小, 吞吐量, 停顿时间之间的平衡点
13 cms垃圾回收器的特点
14 有几种垃圾回收算法
15 计算机网络osi模型
16 tcp如何实现拥塞控制和流量控制?具体算法是什么?
http://blog.csdn.net/honour2sword/article/details/45191115
http://blog.csdn.net/yechaodechuntian/article/details/25429143
17 http头部格式
HTTP请求消息头部实例:
Host:rss.sina.com.cn
User-Agent:Mozilla/5、0 (Windows; U; Windows NT 5、1; zh-CN; rv:1、8、1、14) Gecko/20080404 Firefox/2、0、0、14
Accept:text/xml,application/xml,application/xhtml+xml,text/html;q=0、9,text/plain;q=0、8,image/png,*/*;q=0、5
Accept-Language:zh-cn,zh;q=0、5
Accept-Encoding:gzip,deflate
Accept-Charset:gb2312,utf-8;q=0、7,*;q=0、7
Keep-Alive:300
Connection:keep-alive
Cookie:userId=C5bYpXrimdmsiQmsBPnE1Vn8ZQmdWSm3WRlEB3vRwTnRtW <-- Cookie
If-Modified-Since:Sun, 01 Jun 2008 12:05:30 GMT
Cache-Control:max-age=0
HTTP响应消息头部实例:
Status:OK - 200 <--响应状态码,表示web服务器处理的结果。
Date:Sun, 01 Jun 2008 12:35:47 GMT
Server:Apache/2、0、61 (Unix)
Last-Modified:Sun, 01 Jun 2008 12:35:30 GMT
Accept-Ranges:bytes
Content-Length:18616
Cache-Control:max-age=120
Expires:Sun, 01 Jun 2008 12:37:47 GMT
Content-Type:application/xml
Age:2
X-Cache:HIT from 236-41、D07071951、sina、com、cn <--反向代理服务器使用的HTTP头部
Via:1.0 236-41.D07071951.sina.com.cn:80 (squid/2.6.STABLE13)
Connection:close
18 实现一个用户登录功能,在不使用https的情况下完成安全传输
19 有哪些非对称加密算法
RSA, EI Gamal, ECC, Knapsack, etc.
RSA是企业级应用标准,很多第三方的加密软件使用RSA 2048bit加密。
ECC更高效,164bitECC约等于1024bit的RSA,经常使用在移动设备上。
20 linux了解吗?
21 class字节码文件格式是什么?
ClassFile中的字段简单说明如下:
1、MagicNumber:MagicNumber是用来标志class文件的,虚拟机加载class文件的时候会先检查这四个字节,如果不是cafe babe则虚拟机拒绝加载该文件,这样就可以防止加载非class文件而浪费系统资源。这个字段的长度是4个字节,值是固定的cafebabe。
2、Version:version字段有2个长度都为2字节的字段组成,分别是Major Version和Minor Version,分别代表当前class文件的主版本号和次版本号。随着Java技术的不断发展,Java class文件格式会增加一些新的内容来支持Java语言的新特性。同时,不同的虚拟机支持的Java class文件的版本范围是不同的,所以在加载class文件之前可以先看看该class文件是否在当前虚拟机的支持范围之内,避免加载不支持的class文件。
3、常量池
首先是2个字节的长度字段constant_pool_count,表明常量池包含了多少个常量。
后面跟着就是constant_pool_count个常量,常量池里放的是字面常量和符号引用。
字面常量主要包含文本串以及被声明为final的常量等;符号引用包含类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符,因为java语言在编译的时候没有连接这一步,所有的引用都是运行时动态加载的,所以就需要把这些引用的信息保存在class文件里。
这里的常量又根据具体的类型分成字符串,整形,长整型,浮点型,双精度浮点型这几种基本类型。
而符号引用保存的是对应的引用的全局限定名,所以保存的是字符串。
4、access_flag 保存了当前类的访问权限
5、this_cass 保存了当前类的全局限定名在常量池里的索引
6、super class 保存了当前类的父类的全局限定名在常量池里的索引
7、interfaces 保存了当前类实现的接口列表,包含两部分内容:interfaces_count 和interfaces[interfaces_count]
interfaces_count 指的是当前类实现的接口数目
interfaces[] 是包含interfaces_count个接口的全局限定名的索引的数组
8、fields 保存了当前类的成员列表,包含两部分的内容:fields_count 和 fields[fields_count]
fields_count是类变量和实例变量的字段的数量总和。
fileds[]是包含字段详细信息的列表。
9、methods 保存了当前类的方法列表,包含两部分的内容:methods_count和methods[methods_count]
methods_count是该类或者接口显示定义的方法的数量。
method[]是包含方法信息的一个详细列表。
10、attributes 包含了当前类的attributes列表,包含两部分内容:attributes_count 和 attributes[attributes_count]
class文件的最后一部分是属性,它描述了该类或者接口所定义的一些属性信息。attributes_count指的是attributes列表中包含的attribute_info的数量。
属性可以出现在class文件的很多地方,而不只是出现在attributes列表里。如果是attributes表里的属性,那么它就是对整个class文件所对应的类或者接口的描述;如果出现在fileds的某一项里,那么它就是对该字段额外信息的描述;如果出现在methods的某一项里,那么它就是对该方法额外信息的描述。
22 模拟一个数据库死锁?如何解决死锁问题
http://blog.csdn.net/hexieshangwang/article/details/47189213
23 mysql如何实现分页功能?
24 如果有一百万的数据,mysql如何实现高性能分页
25 数据库三个范式分别是什么?
26 堆排序时间复杂度,nlogn红黑树时间复杂度logn
27 jdk的bin目录下有哪些工具?怎么用?
28 类加载机制是什么,为什么实现双亲委派模型?
http://www.cnblogs.com/wxd0108/p/6681618.html
29 java的异常如何分类?
30 分布式的二阶段提交算法
可用于分布式任务
31 post和get有什么区别
32 ThreadLocal有什么问题 内存溢出 this.thread -> threadlocalmap ->(threadlocal实例,然后value)
33 不加锁实现阻塞队列
2017-9-12
三次握手:
第一次
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
· 客户端先发送FIN,进入FIN_WAIT1状态
· 服务端收到FIN,发送ACK,进入CLOSE_WAIT状态,客户端收到这个ACK,进入FIN_WAIT2状态
· 服务端发送FIN,进入LAST_ACK状态
· 客户端收到FIN,发送ACK,进入TIME_WAIT状态,服务端收到ACK,进入CLOSE状态
· 客户端TIME_WAIT持续2倍MSL时长,在linux体系中大概是60s,转换成CLOSE状态
能不能发送完ACK之后不进入TIME_WAIT就直接进入CLOSE状态呢?不行的,这个是为了TCP协议的可靠性,由于网络原因,ACK可能会发送失败,那么这个时候,被动一方会主动重新发送一次FIN,这个时候如果主动方在TIME_WAIT状态,则还会再发送一次ACK,从而保证可靠性。那么从这个解释来说,2MSL的时长设定是可以理解的,MSL是报文最大生存时间,如果重新发送,一个FIN+一个ACK,再加上不定期的延迟时间,大致是在2MSL的范围。
回到上面的问题,go写了一个HTTP服务,压测发现TIME_WAIT过多。
首先判断是不是压测程序放在服务的同一台机器...当然不会犯这么低级的错误...
那么这个感觉就有点奇怪了,HTTP服务并没有依赖外部mysql或者redis等服务,就是一个简单的Hello world,而TIME_WAIT的是主动断开方才会出现的,所以主动断开方是服务端?
答案是是的。在HTTP1.1协议中,有个Connection头,Connection有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接。还有一个keep-alive的头,设置的值就代表了服务端保持连接保持多久。
HTTP默认的Connection值为close,那么就意味着关闭请求的一方几乎都会是由服务端这边发起的。那么这个服务端产生TIME_WAIT过多的情况就很正常了。
虽然HTTP默认Connection值为close,但是现在的浏览器发送请求的时候一般都会设置Connection为keep-alive了。所以,也有人说,现在没有必要通过调整参数来使TIME_WAIT降低了。
短连接与长连接
通俗来讲,浏览器和服务器每进行一次通信,就建立一次连接,任务结束就中断连接,即短连接。相反地,假如通信结束(如完成了某个HTML文件的信息获取)后保持连接则为长连接。在HTTP/1.0中,默认使用短连接。从HTTP/1.1起,默认使用长连接,这样做的优点是显而易见的,一个网页的加载可能需要HTML文件和多个CSS或者JS,假如每获取一个静态文件都建立一次连接,那么就太浪费时间了,而在保持连接的情况下,继续GET即可。
对于频繁请求资源的客户来说,较适用长连接。但连接数最好进行限制,防止建立太多连接拖累服务端。一般浏览器对一个网站的连接是有限制的几个,所以网站会将资源部署在多个域名上以实现浏览器同时请求。
短/长连接应当在TCP连接的范畴中来讨论。有人常说HTTP的短连接和长连接如何如何,但是HTTP只是一个应用层协议,又是无状态的,最终实质性的保持连接还是得靠传输层,即TCP。
举个例子,NginX作为代理的一种常见配置方式是在NginX与客户端之间的连接使用长连接,NginX与后端服务器之间的连接使用短连接。
keep-alive
我们使用浏览器的开发者工具查看网络请求和响应信息时经常在HTTP请求头部看到Connection: keep-alive,一般的浏览器都会带着个头去请求数据,假如有特殊需求可以用Connection: close断开。HTTP头部的Connection也不一定就被客户端或服务端老老实实地遵循,毕竟各有各的考虑,尤其是在HTTP/1.0这还只是个实验性的功能,而在HTTP/1.1默认长连接于是没有对长连接做特殊的规定。
长连接也不能无限期地长,服务端有可能在头部放Keep-Alive,其中timeout等于一个值来规定保持连接的秒数,还可以用max来规定多少次请求后断开。如果没有说明怎么断开,主动发起四次握手也可以实现连接的断开。
现在有一个问题就是HTTP的keep-alive与TCP的keep-alive到底是什么关系。其实这是两种不同的机制,可以认为没有什么关系。HTTP在头部的Connection中声明keep-alive可以告诉对方要长连接不立即断开,但是TCP的keep-alive则是一种检查对方是否仍旧和自己保持着连接的机制以避免自作多情半开放的连接。假如发出一个探测段,成功收到响应,这证明连接正常保持;假如发出一个探测段一段时间后,一个响应都没收到,对方可能已挂断、机器异常或网络异常;假如对方收到探测段但重置,说明原来的连接已经因为某些原因挂断,目前是因为未进行三次握手新建立连接而被挂断。
算法性能测试?压力测试
互斥锁信号量
信号量与普通整型变量的区别:
①信号量(semaphore)是非负整型变量,除了初始化之外,它只能通过两个标准原子操作:wait(semap) , signal(semap) ;来进行访问;
②操作也被成为PV原语(P来源于Dutch proberen"测试",V来源于Dutch verhogen"增加"),而普通整型变量则可以在任何语句块中被访问;
信号量与互斥锁之间的区别:
1. 互斥量用于线程的互斥,信号线用于线程的同步。
这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源
2.互斥量值只能为0/1,信号量值可以为非负整数。
也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。
3.互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。
项目的系统架构图》》》》
功能的流程图》》》
软件架构方面硬件架构方面
剥洋葱:客户端(技术)服务器(登录服务器业务逻辑服务器数据库服务器)每个服务器的业务需求。
服务器卡顿怎么排查:
free,top,iostat等等看看CPU,内存,网络情况
阻塞”与"非阻塞"与"同步"与“异步"不能简单的从字面理解,提供一个从分布式系统角度的回答。
1.同步与异步
同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。
换句话说,就是由*调用者*主动等待这个*调用*的结果。
而异步则是相反,*调用*在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在*调用*发出后,*被调用者*通过状态、通知来通知调用者,或通过回调函数处理这个调用。
典型的异步编程模型比如Node.js
举个通俗的例子:
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
2.阻塞与非阻塞
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
如果是关心blocking IO/ asynchronous IO, 参考 Unix Network Programming View Book
linux进程间通讯的几种方式的特点和优缺点,和适用场合
由于不同的进程运行在各自不同的内存空间中.一方对于变量的修改另一方是无法感知的.因此.进程之间的信息传递不可能通过变量或其它数据结构直接进行,只能通过进程间通信来完成。
管道 ( pipe ):
管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。 有名管道 (named pipe) :
有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
信号量( semophore ):
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作
为进程间以及同一进程内不同线程之间的同步手段。 # 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识
符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
信号 ( sinal ) :
信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
#共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC
方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,
如信号量,配合使用,来实现进
程间的同步和通信。
# 套接字( socket ) :
套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
管道的主要局限性正体现在它的特点上:
只支持单向数据流;
只能用于具有亲缘关系的进程之间; 没有名字;
管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;
管道通信(PIPE)
两个进程利用管道进行通信时.发送信息的进程称为写进程.接收信息的进程称为读进程。管道通信方式的中间介质就是文件.通常称这种文件为管道文件.它就像管道一样将一个写进程和一个读进程连接在一起,实现两个进程之间的通信。写进程通过写入端(发送端)往管道文件中写入信息;读进程通过读出端(接收端)从管道文件中读取信息。两个进程协调不断地进行写和读,便会构成双方通过管道传递信息的流水线。
利用系统调用PIPE()可以创建一个无名管道文件,通常称为无名管道或PIPE;利用系统调用MKNOD()可以创建一个有名管道文件.通常称为有名管道或FIFO。无名管道是一种非永
久性的管道通信机构.当它访问的进程全部终止时,它也将随之被撤消。无名管道只能用在具有家族联系的进程之间。有名管道可以长期存在于系统之中.而且提供给任意关系的进程使用,但是使用不当容易导致出错.所以操作系统将命名管道的管理权交由系统来加以控制管道文件被创建后,可以通过系统调用WRITE()和READ()来实现对管道的读写操作;通信完后,可用CLOSE()将管道文件关闭。
消息缓冲通信(MESSAGE)
多个独立的进程之间可以通过消息缓冲机制来相互通信.这种通信的实现是以消息缓冲区为中间介质.通信双方的发送和接收操作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称之为消息队列。消息队列一旦创建后即可由多进程共享.发送消息的进程可以在任意时刻发送任意个消息到指定的消息队列上,并检查是否有接收进程在等待它所发送的消息。若有则唤醒它:而接收消息的进程可以在需要消息的时候到指定的消息队列上获取消息.如果消息还没有到来.则转入睡眠状态等待。
共享内存通信(SHARED MEMORY)
针对消息缓冲需要占用CPU进行消息复制的缺点.OS提供了一种进程间直接进行数据交换的通信方式一共享内存 顾名思义.这种通信方式允许多个进程在外部通信协议或同步,互斥机制的支持下使用同一个内存段(作为中间介质)进行通信.它是一种最有效的数据通信方式,其特点是没有中间环节.直接将共享的内存页面通过附接.映射到相互通信的进程各自的虚拟地址空间中.从而使多个进程可以直接访问同一个物理内存页面.如同访问自己的私有空间一样(但实质上不是私有的而是共享的)。因此这种进程间通信方式是在同一个计算机系统中的诸进程间实现通信的最快捷的方法.而它的局限性也在于此.即共享内存的诸进程必须共处同一个计算机系统.有物理内存可以共享才行。
三种方式的特点(优缺点):
1.无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错。
2.消息缓冲可以不再局限于父子进程.而允许任意进程通过共享消息队列来实现进程间通信.并由系统调用函数来实现消息发送和接收之间的同步.从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题.使用方便,但是信息的复制需要额外消耗CPU的时间.不适宜于信息量大或操作频繁的场合。
3.共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点。但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的.因此,这些进程之间的读写操作的同步问题操作系统无法实现。必须由各进程利用其他同步工具解决。另外,由于内存实体存在于计算机系统中.所以只能由处于同一个计算机系统中的诸进程共享。不方便网络通信。
MySQL实现了四种通信协议
1. TCP/IP协议,通常我们通过来连接MySQL,各种主要编程语言都是根据这个协议实现了连接模块
2. Unix Socket协议,这个通常我们登入MySQL服务器中使用这个协议,因为要使用这个协议连接MySQL需要一个物理文件,文件的存放位置在配置文件中有定义,值得一提的是,这是所有协议中最高效的一个。
1、介绍Java中所有的集合;
2、ArrayList和LinkedList的增删改查的效率问题;
3、Set,Map和List集合共同继承什么类;
4、介绍同步锁,使用情景,都可以再哪里使用;
5、假如有两个线程,一个线程A,一个线程B都会访问一个加锁方法,可能存在并发情况,但是线程B访问频繁,线程A访问次数很少,问如何优化。(我第一次遇到这个问题,说不会,然后面试官说有了解过重度锁和轻度锁吗)
6、介绍下线程和进程的关系;
7、进程间都有哪些通信方式;
8、讲一下AIDL;
9、了解Handler机制吗,讲解下;
10、MessageQueue的工作原理;
11、假如有一个一秒和三秒延迟的消息在MessageQueue里面,请问在加入一个两秒延迟的消息插入顺序是怎样的;
12、假如消息队列里有3个消息,分别是延迟1秒,延迟2秒,延迟3秒,如果1秒的已经消耗了500毫秒,这时延迟2秒的会变成1.5秒,并且在插入一个2秒的会排在1.5秒之后,请讲解下这个原理;
13、这个算法,给纸和笔,有一个整数,转换为二进制,请找出所有的1的数量;
14、你觉得Java是完全面相对象的语言吗?
面向对象是一种约束规范,并不是绝对的,比如静态属性比如int这些
15、讲解下你对抽象,多态的理解;
16、讲解下安卓的四大组建,对哪个比较熟;
17、Activity的生命周期;
18、那你讲下,Activity中的onCreate方法里调用finish方法后,会调用哪些方法;
19、讲解下安卓的网络请求,httpclient了解过吗,讲解下Java里调用网络的累都有哪些;
20、讲解下http请求里的header有什么作用;
21、讲解下自己的项目,同时还现场去我的GitHub里看我的开源项目;
22、有什么想问我的,我问了我的缺点和优势;
介绍自己。
Tcp和udp的区别。分别适用于哪些场景。
可靠连接面向数据流面向报文(实时通讯视频)(文件http)
三次握手四次挥手。。是否可以两次握手,为何要四次挥手。
微服务相关。
Get和post区别。
Http协议相关。
Java有没有多继承,接口和抽象类区别。jvm 理解
jre和jdk的区别
数据库四个范式的区别。在设计数据库的时候如何考量。
数据库设计三范式
为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。
在实际开发中最为常见的设计范式有三个:
1.第一范式(确保每列保持原子性)
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。
上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。
2.第二范式(确保表中的每列都和主键相关)
第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。
订单信息表
这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。
而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。
这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。
3.第三范式(确保每列都和主键列直接相关,而不是间接相关)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。
这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。
4.范式总结
第一范式:具有原子性
第二范式:主键列与非主键列遵循完全函数依赖关系
第三范式:非主键列之间没有传递函数依赖关系
数据库设计原则
在您开始阅读这篇文章之前,我得明确地告诉您,我并不是一个数据库设计领域的大师。以下列出的 11 点是我对自己在平时项目实践和阅读中学习到的经验总结出来的个人见解。我个人认为它们对我的数据库设计提供了很大的帮助。实属一家之言,欢迎拍砖 : )
我之所以写下这篇这么完整的文章是因为,很多开发者一参与到数据库设计,就会很自然地把 “三范式” 当作银弹一样来使用。他们往往认为遵循这个规范就是数据库设计的唯一标准。由于这种心态,他们往往尽管一路碰壁也会坚持把项目做下去。
大家都说标准规范是重要的指导方针并且也这么做着,但是把它当作石头上的一块标记来记着(死记硬背)还是会带来麻烦的。以下 11 点是我在数据库设计时最优先考虑的规则。
◆规则 1:弄清楚将要开发的应用程序是什么性质的(OLTP 还是 OPAP)?
当你要开始设计一个数据库的时候,你应该首先要分析出你为之设计的应用程序是什么类型的,它是 “事务处理型”(Transactional) 的还是 “分析型” (Analytical)的?你会发现许多开发人员采用标准化做法去设计数据库,而不考虑目标程序是什么类型的,这样做出来的程序很快就会陷入性能、客户定制化的问题当中。正如前面所说的,这里有两种应用程序类型, “基于事务处理” 和 “基于分析”,下面让我们来了解一下这两种类型究竟说的是什么意思。
事务处理型:这种类型的应用程序,你的最终用户更关注数据的增查改删(CRUD,Creating/Reading/Updating/Deleting)。这种类型更加官方的叫法是 “OLTP” 。
分析型:这种类型的应用程序,你的最终用户更关注数据分析、报表、趋势预测等等功能。这一类的数据库的 “插入” 和 “更新” 操作相对来说是比较少的。它们主要的目的是更加快速地查询、分析数据。这种类型更加官方的叫法是 “OLAP” 。
那么换句话说,如果你认为插入、更新、删除数据这些操作在你的程序中更为突出的话,那就设计一个规范化的表否则的话就去创建一个扁平的、不规范化的数据库结构。
以下这个简单的图表显示了像左边 Names 和 Address 这样的简单规范化的表,怎么通过应用不规范化结构来创建一个扁平的表结构。
◆规则 2:将你的数据按照逻辑意义分成不同的块,让事情做起来更简单
这个规则其实就是 “三范式” 中的第一范式。违反这条规则的一个标志就是,你的查询使用了很多字符串解析函数
例如 substring、charindex 等等。若真如此,那就需要应用这条规则了。
比如你看到的下面图片上有一个有学生名字的表,如果你想要查询学生名字中包含“Koirala”,但不包含“Harisingh”的记录,你可以想象一下你将会得到什么样的结果。
所以更好的做法是将这个字段拆分为更深层次的逻辑分块,以便我们的表数据写起来更干净,以及优化查询。
◆规则 3:不要过度使用 “规则 2”
开发者都是一群很可爱的生物。如果你告诉他们这是一条解决问题的正路,他们就会一直这么做下去,做到过了头导致了一些不必要的后果。这也可以应用于我们刚刚在前面提到的规则2。当你考虑字段分解时,先暂停一下,并且问问你自己是否真的需要这么做。正如所说的,分解应该是要符合逻辑的。
例如,你可以看到电话号码这个字段,你很少会把电话号码的 ISD 代码单独分开来操作(除非你的应用程序要求这么做)。所以一个很明智的决定就是让它保持原样,否则这会带来更多的问题。
◆规则 4:把重复、不统一的数据当成你最大的敌人来对待
集中那些重复的数据然后重构它们。我个人更加担心的是这些重复数据带来的混乱而不是它们占用了多少磁盘空间。
例如下面这个图表,你可以看到 "5th Standard" 和 "Fifth standard" 是一样的意思,它们是重复数据。现在你可能会说是由于那些录入者录入了这些重复的数据或者是差劲的验证程序没有拦住,让这些重复的数据进入到了你的系统。现在,如果你想导出一份将原本在用户眼里十分困惑的数据显示为不同实体数据的报告,该怎么做呢?
解决方法之一是将这些数据完整地移到另外一个主表,然后通过外键引用过来。在下面这个图表中你可以看到我们是如何创建一个名为 “Standards”(课程级别) 的主表,然后同样地使用简单的外键连接过去。
◆规则 5:当心被分隔符分割的数据,它们违反了“字段不可再分”
前面的规则 2 即“第一范式”说的是避免 “重复组” 。下面这个图表作为其中的一个例子解释了 “重复组”是什么样子的。如果你仔细的观察 syllabus(课程) 这个字段,会发现在这一个字段里实在是填充了太多的数据了。像这些字段就被称为 “重复组” 了。如果我们又得必须使用这些数据,那么这些查询将会十分复杂并且我也怀疑这些查询会有性能问题。
这些被塞满了分隔符的数据列需要特别注意,并且一个较好的办法是将这些字段移到另外一个表中,使用外键连接过去,同样地以便于更好的管理。
那么,让我们现在就应用规则2(第一范式) “避免重复组” 吧。你可以看到上面这个图表,我创建了一个单独的 syllabus(课程) 表,然后使用 “多对多” 关系将它与 subject(科目) 表关联起来。
通过这个方法,主表(student 表)的 syllabus(课程) 字段就不再有重复数据和分隔符了。
◆规则 6:当心那些仅仅部分依赖主键的列
留心注意那些仅仅部分依赖主键的列。例如上面这个图表,我们可以看到这个表的主键是 Roll No.+Standard。现在请仔细观察 syllabus 字段,可以看到 syllabus(课程) 字段仅仅关联(依赖) Standard(课程级别) 字段而不是直接地关联(依赖)某个学生(Roll No. 字段)。
Syllabus(课程) 字段关联的是学生正在学习的哪个课程级别(Standard 字段)而不是直接关联到学生本身。那如果明天我们要更新教学大纲(课程)的话还要痛苦地为每个同学也修改一下,这明显是不符合逻辑的(不正常的做法)。更有意义的做法是将这些字段从这个表移到另外一个表,然后将它们与 Standard(课程级别)表关联起来。
你可以看到我们是如何移动 syllabus(课程)字段并且同样地附上 Standard 表。
这条规则只不过是 “三范式” 里的 “第二范式”:“所有字段都必须完整地依赖主键而不是部分依赖”。
◆规则 7:仔细地选择派生列
如果你正在开发一个 OLTP 型的应用程序,那强制不去使用派生字段会是一个很好的思路,除非有迫切的性能要求,比如经常需要求和、计算的 OLAP 程序,为了性能,这些派生字段就有必要存在了。
通过上面的这个图表,你可以看到 Average 字段是如何依赖 Marks 和 Subjects 字段的。这也是冗余的一种形式。因此对于这样的由其他字段得到的字段,需要思考一下它们是否真的有必要存在。
这个规则也被称为 “三范式” 里的第三条:“不应该有依赖于非主键的列” 。 我的个人看法是不要盲目地运用这条规则,应该要看实际情况,冗余数据并不总是坏的。如果冗余数据是计算出来的,看看实际情况再来决定是否应用这第三范式。
◆规则 8:如果性能是关键,不要固执地去避免冗余
不要把 “避免冗余” 当作是一条绝对的规则去遵循。如果对性能有迫切的需求,考虑一下打破常规。常规情况下你需要做多个表的连接操作,而在非常规的情况下这样的多表连接是会大大地降低性能的。
◆规则 9:多维数据是各种不同数据的聚合
OLAP 项目主要是解决多维数据问题。比如你可以看看下面这个图表,你会想拿到每个国家、每个顾客、每段时期的销售额情况。简单的说你正在看的销售额数据包含了三个维度的交叉。
为这种情况做一个实际的设计是一个更好的办法。简单的说,你可以创建一个简单的主要销售表,它包含了销售额字段,通过外键将其他所有不同维度的表连接起来。
◆规则 10:将那些具有“名值表”特点的表统一起来设计
很多次我都遇到过这种 “名值表” 。 “名值表” 意味着它有一些键,这些键被其他数据关联着。比如下面这个图表,你可以看到我们有 Currency(货币型)和 Country(国家)这两张表。如果你仔细观察你会发现实际上这些表都只有键和值。
对于这种表,创建一个主要的表,通过一个 Type(类型)字段来区分不同的数据将会更有意义。
◆规则 11:无限分级结构的数据,引用自己的主键作为外键
我们会经常碰到一些无限父子分级结构的数据(树形结构?)。例如考虑一个多级销售方案的情况,一个销售人员之下可以有多个销售人员。注意到都是 “销售人员” 。也就是说数据本身都是一种。但是层级不同。这时候我们可以引用自己的主键作为外键来表达这种层级关系,从而达成目的。
这篇文章的用意不是叫大家不要遵循范式,而是叫大家不要盲目地遵循范式。根据你的项目性质和需要处理的数据类型来做出正确的选择。
详细设计数据库步骤。
一、关于java基础
1、一致性hash
(1)提出一致性hash是为了解决什么问题?
(2)hash的性能评估指标(单调性、容错性、可扩展性)
2、java锁机制
(1)synchronized关键字、volatile关键字
(2)java.util.concurrent.locks包
(3)synchronized和ReentrantLock(重入锁)的区别,synchronized用在代码块、方法、静态方法时锁的都是什么
3、ConcurrentHashMap的原理,都用的是哪种锁,segment有没可能增大(不会,只会增大每个segment中的entry数组)
4、进程和线程的区别
5、AtomicInteger实现原理(CAS自旋)
6、值传递和引用传递
7、java有没有多继承,接口和抽象类区别
8、jdk和jre的区别
9、讲述一下并发编程与多线程
10、String、StringBuffer和StringBuilder的区别
11、int在32位和64位机中的区别
12、java中基本类型的位数
13、static关键字的用法
14、java的访问权限,成员的可见性
15、HashmMap、HashTable、ConcurrentHashMap的区别与联系
16、char能否存储汉字?
17、Spring转成int型,能不能转,怎么转
18、HashMap和HashSet的实现原理
19、动态代理的原理
20、包装类型和基本数据类型的比较问题(例如:Integer能否== int类型变量,能否做比较,什么时候不能做比较)
21、ArrayList和LinkedList的比较
22、单例模式都有什么,是否线程安全,怎么改进(从synchronized到双重检验锁到枚举)
23、java线程安全都体现在哪些方面,如何维护线程安全?
24、反射机制中可以获取private成员的值吗(没有set和get函数)
25、写一段synchronized可能发生死锁的代码
26、死锁的条件,摒弃死锁的条件有哪些?
二、关于java虚拟机
1、垃圾回收算法,为什么要分代处理
2、GC停顿原因,如何降低停顿
3、JVM如何调优,参数怎么调
4、如何使用工具分析jvm状态
5、类加载机制
6、什么是java虚拟机,对jvm的理解
7、jvm内存模型,各个区的作用
8、堆的作用
9、JVM调试工具
10、堆和栈的区别
11、内存溢出了怎么办
12、什么时候会栈溢出
三、关于java后台
1、介绍Spring的IOC和AOP,分别是如何实现的(反射机制和动态代理)
2、redis如何处理分布式服务器并发造成的不一致,如果数据库不提供隔离呢?
3、解释MySQL索引、b树,为啥不用平衡二叉树或红黑树(磁盘与内存的存储方式不同)
4、Spring的bean加载机制,bean生成的具体步骤
5、IOC的注入方式(set注入、构造方法注入)
6、Spring何时创建applicationContext(web.xml中使用listener)
7、listener是监听哪个事件
8、SpringMVC的具体流程
9、Spring的特性,SpringMVC中控制器的名字及其配置
10、Spring用到哪些设计模式?
11、controller怎么处理的请求
四、关于数据结构与算法
1、01矩阵,相邻的1是一个岛屿,找出所有岛屿数(递归解决)
2、2个鸡蛋,100高的楼,最少的次数测出鸡蛋碎掉的临界层,没碎的鸡蛋可以反复使用
3、大量字符串找出数量最多的K个,考虑内存放得下和放不下两种情况
4、跳表的基本原理
5、最长回文子串
6、找到数组中最多的那个数,除了hashset还有没有其他方法(蒙特卡罗法)
7、输入合法的数字字符串,输出货币字符(就是每隔三位加逗号,考虑边界处理)
8、有1-10 10个数,和一个数sum,计算用10个数连加得到sum有多少种系数组合(递归)
9、如何实现高效的同步链表
10、层次遍历二叉树,偶数层输出,且逆序输出
11、给定一个字符串,写出所有可能的全排列(递归)
12、5亿条淘宝交易订单,输出销售次数最多的100个商品。
13、给出年月日,如何求出这天是这年的第几天?
14、64个马有8个跑道,没有计时工具,最少需要多少次跑,可以找到跑的最快的那个马,如果换成找到最快的四匹呢?
15、给一个函数,返回0和1,概率为p和1-p,请你实现一个函数,使得返回0和1的概率一样
16、10亿个url,每个url大小小于56B,要求去重,内存4G
17、吧一个BST转化成一个双向链表
18、手写一个全排列
19、B数和B+数
20、介绍一下hash,怎么解决hash冲突
21、给一个文件,找出其中每个单词的个数
22、1000瓶药有1瓶有毒,如果小白鼠服用有毒的药,则24小时后死亡。现在需设计一种策略,使用尽可能少的小白鼠,在24小时内找出有毒的药。(将串行的二分法并行使用)
五、关于Http协议
1、TCP和UDP的区别,分别适用于哪些场景?
2、三次握手四次握手,是否可以两次握手,为何要四次握手?
3、get和post的区别
4、Http协议相关
六、关于Linux
1、epoll、epollh和select的区别
2、Linux下的一些指令,怎么查看进程,按照内存大小,CPU占用排序等
七、关于数据库
1、数据库四个范式的区别,在设计数据库的时候怎么考量,详细设计数据库的步骤。
2、MySQL和Oracle的区别,如何选择?
3、SQL中replace和update的区别
4、事务管理acid
5、数据库的索引有什么用,带来的问题是什么?
6、数据库查询时间优化
7、
八、设计模式
1、单例模式
2、生产者模式、消费者模式
文件A100亿记录,文件B100万记录,找出A中不在B中的记录
如何拦截5分钟前恶意登录(用户名密码不正确)超多一定次数的ip
12、数据传输:服务器与服务器之间传输文件夹下的文件,一个文件夹下有10个文件,另一个文件夹下有100个文件,两个文件夹大小相等,问,哪个传输更快?(http协议)
13、数据库两个表关联的方式
14、数据库表关联的内部算法
15、数据库锁的实现
16、数据库表里有100万条数据,想要删除80万条数据,但是因为锁的原因,删除很慢,现在想要快速删除怎么办
网易内推
一面(项目)
1. 项目的架构,怎么实现的
2. 会不会java web的知识
3. 对一个登录窗口进行测试
4. 测试人员需要的能力
5. 有没有比较熟的b/s架构
二面(测试思维)(未通过)
1. 写代码:找出一组数中最大的数
2. 所接触到的测试的内容
3. 对淘宝的搜索栏进行测试(1.宝贝,店铺,天猫选项;2.搜索栏;3.搜索按钮)
4. 与他人相比,你的优势(此处最好答热情和测试行业大趋势和前景)
中兴
一面(现场+技术)
1.简单介绍一下自己
2.简单介绍一下项目
3.性能测试有哪些
4.TCP的拥塞控制
5.http中的get和post请求的区别
6.怎么确认post请求发送完毕
7.专利是第一作者么
8.loadrunner用来干了什么
二面(现场+综合面试)
1.英语介绍自己的学校
2.遇到过最大的挫折
3.看什么书,重点讲一本
4.自己的优缺点
5.如果你带领一个团队做一款产品,你想做什么,为什么(后面会有无数个坑,压力面的核心)
6.怎么调节负面情绪
7.有什么爱好
58面试
一面(基础)
1.介绍一下项目
2.流行的加密算法(Lz是信息安全方向的硕士,很多面试官都会稍微考考信息安全的问题,考察一下对专业知识的了解)
3.写代码(找出数组中不重复的数)
4.写上述代码的测试用例
5.SQL(写语句,top,count,group by,order by,select)
6.linux(cd..,cd-,mkdir,vi,netstat)
7.java(忘记了)
8.网络(get和post的区别,七层分别是什么,应用层的协议)
9.测试(测试一个系统的性能,具体不记得了)
二面(基础)
1.自我介绍
2.写代码(单例模式)
3.SQL(索引,多表查找)
4.数组和链表的区别
5.hash的用处
6.怎么解决hash冲突
7.linux就不问了
8.DNS劫持的原理
三面(HR)
1.自我介绍
2.有什么符合软件测试岗位的优势
3.用三个词形容自己
4.遇到过什么困难
5.有没有考虑过离乡背井的艰难
美团
一面(基础+项目)
1.介绍项目
2.项目中的模糊检索怎么实现(项目挖的比较深,会一直追着问)
3.项目界面和数据库怎么交互?数据流向
4.项目怎么测试
5.软件测试的基本流程
6.TCP和UDP的区别和应用场景
7.写代码(一个数的个位十位百位相加,判断结果的个位是不是3)
二面(基础+算法)
1.自我介绍
2.讲一下项目
3.java回收机制
4.java内存泄露
5.重载和重写的区别
6.http的关键字
7.TCP和UDP的区别
8.写一个SQL语句
9.group by和order by的区别
10.写代码(全排列)
11.写代码(A to I)
12.上一个代码的测试用例
13.1G数据怎么排序
三面(总监+宏观把控)
1.cookie和session的区别(总监面的每个问题都会问的比较深,延伸得比较广;而且每个问题回答完之后都会着“你确定?再想一想”)
2.session存在服务器的哪个地方
3.在地址栏键入网址之后的过程是什么样的,数据流向,哪一个过程最耗时间
4.怎么优化数据库
5.平时怎么学习的
6.java和c++最大的区别
7.有些什么测试方法
8.路由表中存的是什么
9.B/S和C/S架构的区别
10.有没有用过框架
四面(现场+HR)
1.自我介绍
2.最有成就的事
3.遇到过什么挫折
4.优缺点
5.对工作地点有没有要求
6.最想去哪家互联网公司,有什么地方吸引你
7.对美团有什么了解
8.平时喜欢干什么
9.有什么问题
百度
一面(基础)
1.写代码(从数组中找出两个相加等于n的下标)
2.测试三边能不能组成三角形(考测试思维全面性和逻辑是否清晰)
3.为什么报测试
4.常见的测试类型,性能测试指标
5.SQL(多表查询,左链接和右链接的区别)
6.linux(查看一个文档,显示出文档的最后五条数据)
7.JAVA(int和Integer的区别)
8.网络(搜索百度主页的全过程,整个过程的物理设备,网路协议,数据流向;session)
9.怎么与人合作的
10.怎么证明自己学习能力强
11.作为测试的优势
12.怎么考虑工作地点离家远的问题
13.有什么要问的
二面(处理能力+思维全面性)
1.自我介绍
2.项目的具体算法
3.怎么判断链表有环,并优化(要求写出时间复杂度最优的算法)
4.怎么测试string to int程序(要说的很全面,一直问“还有吗”)
5.平时有什么爱好
6.对测试的了解
7.做测试的优势
8.在一个环形商场里有很多商家,商家的钱数事先知道。让你去抢钱,并且保证不抢连续两家,怎么保证抢钱数目最多
9.让你去实习,可以去么
10.有什么要问的
三面(总监+宏观把控)
1.自我介绍一下
2.你怎么学的测试
3.简单介绍一下项目
4.对工作地点是怎么选择的
5.对测试的一些想法
6.优缺点
7.有什么要问的
滴滴
一面(基础)
1.简单自我介绍一下
2.项目介绍
3.对测试的理解
4.java中的框架
5.了解到的锁的知识
6.进程间通信的方式
7.写代码(二叉树的非递归后续遍历)
8.对上面的代码写单测
9.linux相关
10.数据库的优化
11.线程的状态转化
12.TCP和UDP的区别
13.详细讲讲拥塞控制
14.java中的集合的底层实现
二面(算法)
1.三人三鬼安全运过河(不详述)
2.两个同样大小的罐子,一个装盐,一个装糖。首先从盐罐舀一勺盐到糖罐,再将糖罐中的混合物搅拌均匀,再从糖罐舀一勺混合物到盐罐,问,最后是糖罐的盐多还是盐罐的糖多(用公式计算的还不行,要一下就能得出结果的想法)
3.写代码(判断字符串数组是否可以收尾相连成环路)
4.写代码(从int型数组中找出和连续子串,要求和最大)
5.测一个存储系统B(系统B有上级系统A,系统B中有1T存储容量,每天0点定时更新,还有分布式的缓存……)
三面(现场+交叉面)(未通过)
1.自我介绍
2.什么是有效的沟通
3.对一个功能进行压力测试
4.滴滴抢单给出了新思路(大概是就用抢单之后会等10s,对抢单的司机进行最优匹配),让设计整个架构,并列出测试点
5.对滴滴的了解
总结:1.现场要求写代码,最好先理清思路,考虑一下时间复杂度和空间复杂度,能写出最优算法是会加分不少的。(这儿也有坑,某公司的面试官让写了个代码,看完之后问“有没有更加优化的代码?”Lz又看了大概5分钟,确实没想出来。答“想不出来。”面试官笑着说“想不出来是因为的确没有更优的了。我还真怕你给我胡诌一个出来。”Lz“……”。所以回答问题实事求是,按正常的思路走,不会的时候不要乱说);
2.对于每个知识点,最好说出自己的理解,不要直接背书上的1234点。就拿java中问的比较常见“抽象类和接口的区别”举例。如果你回答1.java中抽象类只能单继承,但可以实现多个接口,2.抽象类中可以有方法的定义与实现,接口中方法不能实现………………这样的回答大家都会,所以如果说完上述这些,再加入一点自己实际学习中的理解会更好。比如:通常,我会把phone定义为一个抽象类,它是各种不同手机类的一个抽象。然而有的手机具有指纹解锁的功能,有的手机具有防水防尘的功能,对于这些附加的功能,我会把它们分别定义成不同的接口。当我需要这个功能的时候,在具体的类中再去实现这个功能。这样会更加符合面向对象中高内聚低耦合的特点…………就是这类似这样的回答,会更加突出你学习中的思考过程。
3.既然投了测试,肯定会问测试相关的知识,所以还是得早点看看书,多看看博客,逛逛论坛;
4.面试官都很不错,有的时候会给你一些提示和引导,所以放轻松,太紧张只会思路混乱;
5.一般面试的酒店可能会比较远,保证比约定时间早到30分钟就行了。签到之后整理一下自己的衣着,整洁干净就行。看看笔记,调动一下思维。
6.最后面试官问你有什么问题的时候一定要问问,Lz之前被某公司内推时的一个HR宣传视频误导了,以为最后纯粹是面试官客套一下,不用问。之后跟一些面试官的沟通过程中发现应聘者问的问题还是挺重要的,这个问题会暴露你的关注点。尽量让面试官了解到你对未来工作细节的关注,让他知道你对这个工作很感兴趣。但是最好不要问关于薪酬,福利的问题,等到谈offer的时候自然会跟你说这些。
1、HTTP请求的过程,越清楚越好(我从dns解析到TCP三次握手,四次挥手,还有后台请求逻辑讲了一遍)
2、Java线程池ThreadPoolEcecutor参数,基本参数,使用场景
3、HashMap、HashTable、CurrentHashMap的区别及场景,说一下你的理解。HashMap的负载因子,干啥用的。
4、ArrayList与LinkedList的区别及使用场景,有没有ArrayList增删效率比LinkedList高的场景。ArrayList底层采用什么实现数据的拷贝。
5、MySQL的ACID讲一下,延伸到隔离级别
6、JMM讲一下,volite关键字及使用场景。
7、JVM的内存布局以及垃圾回收原理及过程讲一下。讲一下CMS垃圾收集器垃圾回收的流程。以及CMS的缺点(忘了。。)
8、 智力题(聊着聊着突然来个),两个杯子,一个有3升水,一个有5升水,请问如何倒出4升水。。(我回答说,使两者水平面一致。。。。一看到题,就这样啊。。但其答案不是我回答的这。。)
9、 手写二叉树的中序遍历与层序遍历
10、 手写二分查找(很基础)
11、最后还聊了一下项目,聊一下难点。
12、最后就是你有什么要问的。1、表现2、你们那边主要用那些java技术3、如果过,啥时通知下一面结果。