Java经典问题总结

精华:
美团点评技术团队文章
常用的资源库
1。HashMap和HashTable的区别,及其实现原理。
ArrayList,LinkedList 和Vector的区别和实现原理。
TreeMap和TreeSet区别和实现原理。
ConcurrentHashMap实现原理(锁分离技术)。
2。问jvm内存分代机制(会问分为那几个代,各个代特点),分代回收的优点(这个问了很多次)。
3。String和StringBuffer,StringBuilder区别和联系,String为啥不可变,在内存中的具体形态。 参考链接
4。java中多线程机制,实现多线程的两种方式(继承Thread类和实现Runnable接口)的区别和联系。
5。java线程阻塞调用wait函数和sleep区别和联系,还有函数yield,notify等的作用。
6。java中的同步机制,synchronized关键字,锁(重入锁)机制,其他解决同步的方volatile关键字ThreadLocal类的实现原理要懂。
7。java中异常机制, 如何正确处理异常, 可以举例说明吗?
9。comparable接口和comparator接口实现比较的区别和用法,Arrays静态类如下实现排序的。
网络方面TCP,HTTP要明白,进程和线程的却别联系
(线程拥有哪些自己的资源,这个问题面腾讯时直接说错了,然后就挂了)
11。volatile实现原理?
12。protected权限能否被包外访问,然后问boolean占几个字节,long占几个字节?(java的数据类型,占用内存大小)
13。spring的一些知识,如:有哪些注入方式?
14。问了浏览器如何实现免登陆之类的功能,进而问了cookie和session相关的知识
15。http和https区别(https用了什么证书,怎样在网络传输中实现,我当时回答了osi七层,TCP udp ,三次握手,四次断开)
16。java反射机制
17。什么是JNA(Java Native Access )?
17。设计模式的六大原则是什么?如何理解?
设计模式六大原则(1):单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
设计模式六大原则(2):里氏替换原则:子类可以扩展父类的功能,但不能改变父类原有的功能。
设计模式六大原则(3):依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象(接口);抽象不应该依赖细节;细节应该依赖抽象。
设计模式六大原则(4):接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。接口设计的过大或过小都不好。
设计模式六大原则(5):迪米特法则:一个对象应该对其他对象保持最少的了解。迪米特法则又叫最少知道原则
设计模式六大原则(6):开闭原则:个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
总结:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
参考:设计模式六大原则 参考书籍:《设计模式》《设计模式之禅》《大话设计模式》
18。常用的设计模式
19。用一个设计模式写一段代码或画出一个设计模式的UML
20。如何理解MVC
21。高内聚,低耦合方面的理解
22。JavaEE
23。如何合理地估算线程池大小?
参考链接:http://ifeve.com/how-to-calculate-threadpool-size/
考虑cpu的个数,期望cpu利用率, io密集型任务, cpu密集型任务,当前系统是不是有其他的任务。
在《Java Concurrentcy in Practise》给出的经验公式如下:
Ncpu = CPU的数量
Ucpu = 目标CPU的使用率, (0<=Ucpu<=1)
W/C = 等待时间和计算时间的比率
为保持处理器达到期望的使用率, 最优的池的大小等于:
Nthreads = Ncpu * Ucpu * (1 + W/C)
24。Java对象的内存布局以及对象所需内存大小计算详解
servlet生命周期及各个方法
25。Java中的锁——队列同步器
servlet中如何自定义filter
26。写出Integer类中的toBinaryString()方法

public class TestToBinaryString {
    public static void main(String[] args) {
        System.out.println(toBinaryString(-5));
    }
    public static String toBinaryString(int i) {
        return toUnsignedString(i, 1);
    }
    private static String toUnsignedString(int i, int shift) {
        char[] buf = new char[32];
        int charPos = 32;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            //这里的mask一直为:1,所以当i为奇数的时候,这里"i & mask"操作才为:1
            //否则返回:0
            //System.out.println(i & mask); 
            buf[--charPos] = digits[i & mask];
            i >>>= shift;//右移赋值,左边空出的位以0填充
        } while (i != 0);
        return new String(buf, charPos, (32 - charPos));
    }
    final static char[] digits = {
        '0' , '1' 
        };
}

27。在Java中>>、>>>的区别
我的理解:
1。>>代表算术移位也就是用符号位填充最高位。
2。>>代表逻辑移位即用0填充最高位。
3。对于正整数两者基本相似
4。对于负数,算术移位后扔是负数但是逻辑移位后不保证是负数
Integer.toBinaryString(num)可以把正型输出为补码。
原码:是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。
反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
反码通常用来作为原码求补码或者补码求源码的过渡。
补码:计算机中用补码表示。
对于正数,补码和原码的表示相同
对于负数,原码符号位不变, 数值部分按位取反,末位加1(即“取反加1”)此规则同样用于补码求原码。
题目:变量a是一个64位有符号的整数,初始值用16进制表示为:0x7FFFFFFFFFFFFFFF;
变量b是一个64位有符号的整数,初始值用16进制表示为:0x8000000000000000。
则a+b的结果用10进制表示为多少?
理解:其中计算机中的数值用补码表示,所以a和b都是补码
0x7FFF,FFFF,FFFF,FFFF + 0x8000,0000,0000,0000 = 0xFFFF,FFFF,FFFF,FFFF
然后将 0xFFFF,FFFF,FFFF,FFFF转为原码(初符号位以外取反加1)等于0x1000,0000,0000,0001即为-1

public class App 
{
    public static void main( String[] args )
    {
        long a = 0x7FFFFFFFFFFFFFFFL;
        System.out.println(Long.toBinaryString(a));
        long b = 0x8000000000000000L;
        System.out.println(Long.toBinaryString(b));
        System.out.println(a+b);
        long c = a + b;
        System.out.println(Long.toBinaryString(c));
    }
}

详细介绍请参考:http://www.cnblogs.com/wxdlut/p/5881865.html
28。Spring支持哪些依赖注入的方式?有什么区别?
参考:P121《精通Spring4.X企业应用开发实战》
JSP原理
JSP和Servlet的区别
JSP的动态include和静态include
Struts中请求处理过程
MVC概念
Spring mvc与Struts区别
Hibernate/Ibatis两者的区别
Hibernate一级和二级缓存
Hibernate实现集群部署
Hibernate如何实现声明式事务
简述Hibernate常见优化策略
Spring bean的加载过程(推荐看Spring的源码)
Spring如何实现AOP和IOC
Spring bean注入方式
Spring的事务管理(推荐看Spring的源码)
Spring事务的传播特性
springmvc原理
springmvc用过哪些注解
Restful有几种请求
Restful好处
Tomcat,Apache,JBoss的区别
memcached和redis的区别
有没有遇到中文乱码问题,如何解决的
如何理解分布式锁
你知道的开源协议有哪些
json和xml区别
链接:https://www.nowcoder.com/discuss/3043
J2SE基础
1.九种基本数据类型的大小,以及他们的封装类。
2.Switch能否用string做参数?
3.equals与==的区别。
4.Object有哪些公用方法?

public class Object {
    private static native void registerNatives();
    static {
        registerNatives();
    }
    public native int hashCode();
    public boolean equals(Object obj) {
        return (this == obj);
    }
    protected native Object clone() throws CloneNotSupportedException;
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    public final native void notify();
    public final native void notifyAll();
    public final native void wait(long timeout) throws InterruptedException;
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
    public final void wait() throws InterruptedException {
        wait(0);
    }
    protected void finalize() throws Throwable { }
}

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。
22.线程同步的方法:sychronized、lock、reentrantLock等。
23.锁的等级:方法锁、对象锁、类锁。
24.写出生产者消费者模式。
25.ThreadLocal的设计理念与作用。
26.ThreadPool用法与优势。
27.Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
28.wait()和sleep()的区别。
29.foreach与正常for循环效率对比。
30.Java IO与NIO。
31.反射的作用于原理。
32.泛型常用特点,List能否转为List。
33.解析XML的几种方式的原理与特点:DOM、SAX、PULL。
34.Java与C++对比。
35.Java1.7与1.8新特性。
36.设计模式:单例、工厂、适配器、责任链、观察者等等。
37.JNI的使用。
38.谈谈你对Java浅克隆与深克隆的理解?(c++的浅拷贝和深拷贝)
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虚拟机》
面向对象和面向过程的区别
JAVA基础
Java的四个基本特性(抽象、封装、继承,多态)
Overload和Override的区别
构造器Constructor是否可被override
访问控制符public,protected,private,以及默认的区别
是否可以继承String类
String和StringBuffer、StringBuilder的区别
请问hashCode和equals方法的关系?
抽象类和接口的区别
自动装箱与拆箱
什么是泛型、为什么要使用以及泛型擦除
Java中的集合类及关系图
HashMap实现原理(看源代码)
HashTable实现原理(看源代码)
Concurrenthashmap实现原理(看源代码)
HashTable如何实现线程安全(看源代码)
HashMap和HashTable区别
ArrayList和vector区别(看源代码)
ArrayList和LinkedList区别及使用场景
Collection和Collections的区别
Error、Exception区别
Unchecked Exception和Checked Exception,各列举几个
Java中如何实现代理机制(JDK、CGLIB)
多线程的实现方式
线程的状态转换
如何停止一个线程
什么是线程安全
如何保证线程安全
Synchronized如何使用
synchronized和Lock的区别
多线程如何进行信息交互
sleep和wait的区别(考察的方向是是否会释放锁)
多线程与死锁
如何才能产生死锁
什么叫守护线程,用什么方法实现守护线程
Java线程池技术及原理,如何实现一个线程池
java并发包concurrent及常用的类
volatile关键字
Java中的NIO,BIO,AIO分别是什么
IO和NIO区别
序列化与反序列化
常见的序列化协议有哪些
内存溢出和内存泄漏的区别
Java内存模型及各个区域的OOM,如何重现OOM
出现OOM如何解决
用什么工具可以查出内存泄漏
Java内存管理及回收算法
Java类加载器及如何加载类,判断两个对象是否相等。(双亲委派)

比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载它们才有意义,否则,即使两个类来源同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
双亲委派模型
1. 启动类加载器(Bootstrap ClassLoader)C++编写
2. 扩展类加载器 (Extension ClassLoader)一般对应jre\lib\ext
3. 应用程序类加载器(Application ClassLoader)加载classpath指定的类,是最长使用的一种加载器

xml解析方式
Statement和PreparedStatement之间的区别
参考链接:http://www.jianshu.com/p/05f42258850b
参考博客:http://www.cnblogs.com/skywang12345/category/455711.html
作者:牛客249245号
链接:https://www.nowcoder.com/discuss/12476
来源:牛客网

1.hashMap实现原理。扩充时候是否允许插入?原始长度为什么设置为16?hashMap问了将近20分钟
2.hashTable与concurrentHashMap区别。怎么实现线程安全
3.collection接口中应用中遇到过什么问题?怎么解决的
4.map接口中的TreeMap/linkedHashMap区别
5.线程池怎么实现的?线程池中线程的数量怎么设定?jdk怎么监控线程状态与线程数量?jstack打印线程状态,分析线程负载况?linkedBlockingQuene与ArraylistQuene区别
6.GC算法?CMS垃圾收集器,CMS垃圾收集中断几次?
7.mysql 索引原理。为什么索引一部分放到内存,一部分放到硬盘上?
8.看过哪些JDK源码
9.介绍下封装类源码
10.介绍异常类。
Try,catch,finally?finally一定会执行吗?
11.看代码,写输出。考察初始化,静态代码块,
i++与++i,静态变量
12.做过相关项目
作者:牛客249245号
链接:https://www.nowcoder.com/discuss/12476
来源:牛客网
1.jvm原理。程序运行区域划分
2.minor GC 与Full GC。分别采用哪种垃圾回收算法?简单介绍算法
3.HashMap实现原理
4.java.util.concurrent包下使用过哪些
5.concurrentMap 和HashMap区别
6.信号量是什么,怎么使用
7.阻塞队列了解吗?怎么使用
8.JAVA NIO是什么
9.类加载机制是怎样的
10.什么是幂等性
11.有哪些JVM调优经验
12.分布式CAP了解吗?
20.mysql有哪些存储引擎?各自特点
21.用过哪些设计模式?怎样实现线程安全单例模式?
22.用过哪些RPC框架
23.什么是AOP
24.java垃圾回收会出现不可回收的对象吗?怎么解决内存泄露问题?怎么定位问题源?
25.终止线程有几种方式?终止线程标记变量为什么是valotile类型?
26.用过哪些并发的数据结构?cyclicBarrier什么功能?信号量作用?数据库读写阻塞怎么解
27.乐观锁与悲观锁。怎么实现乐观锁
28.开发过分布式框架?怎么实现分布式事务?
29.项目介绍,画出其中一个项目架构图,问了项目中很多技术细节
30.写算法。找到最大子数组的start,和end下标
31.智力题。5升桶3升桶搞4升水
32。是否了解过高性能队列Disruptor?
参考:高性能队列——Disruptor

1.mysql 索引原理。为什么索引一部分放到内存,一部分放到硬盘上?
2.mysql有哪些存储引擎?各自特点 ?mysql默认的存储引擎是什么?
MySQL5.5以后默认使用InnoDB存储引擎,其中InnoDB和BDB提供事务安全表
MySql数据库索引原理

3.类加载的五个过程:加载、验证、准备、解析、初始化。
先用javac Test.java进行编译。然后用java -XX:+TraceClassLoading Test。
然后就能在控制台打印出一大串的log。
在eclipse中查看类加载过程步骤:
项目–>properties–>run/debug setting–>选择目标类Test.java–>edit–>arguments–>VM arguments–>
输入:-XX:+TraceClassLoading。
类加载过程:
类从被加载到虚拟机到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括:
加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),
初始化(Initialization),使用(Using)和卸载(Unloading)7个阶段。
其中验证,准备,解析这3个部分统称为链接(Linking)。
类加载的过程:加载,验证,准备,解析,初始化这5个过程
加载阶段,虚拟机完成以下3件事情:
1. 通过一个类的全限定名来获取定义此类的二进制字节流
2. 将这个字节流所代表的静态结构转化为方法区的运行时数据结构
3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问的入口。
验证阶段:验证目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机规范,并且不会危害虚拟机自身安全。
如果验证到输入的字节流不符合Class文件的约束,会抛出java.lang.VerifyError异常或其子类异常。
验证阶段大致分为4个阶段完成:
1. 文件格式验证 2.元数据验证 3.字节码验证 4.符号引用验证
1. 文件格式验证:魔术,主次版本号,常量池类型等等
2. 元数据验证:对字节码描述的信息进行语义分析,确保符号规范
比如:是否有父类,是否继承final修饰的类,是否实现了接口中的方法等等
3. 字节码验证:这个验证最复杂的部分,目的是通过数据流和控制流分析,确保程序语义合法,符合逻辑。在第二阶段元数据中数据类型做完验证后,这个阶段对类的方法体进行校验分析。
保证操作数栈的数据类型与指令代码序列可以配合工作,不会出现操作数栈放置一个int类型,使用时按long类型加载本地变量表。
4. 符合引用验证:发生在虚拟机将符号引用转化为直接引用的时候。比如符号引用中通过字符串描述的全限定名是否能查找到相应的类,符号引用中的类,字段,方法的访问性是否可以被当前类方法。
符号引用验证的目的是确保解析动作能正常执行
对于虚拟机的类加载机制来说,验证阶段是一个非常重要的,但不是一定必要的阶段。(因为对程序运行期没有影响)。
准备阶段:
准备阶段是正式为类的变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。注意:这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,这里所说的初始值“通常情况”下是数值类型的零值。
相对“通常情况”会有一些特殊情况:如果类字段的字段属性存在ConstantValue属性,那在准备阶段变量会被初始化为ConstantValue属性所指定的值。
比如:public static final int value = 123;
编译时javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123
解析阶段:
解析阶段是虚拟机将常量池的符合引用替换为直接引用的过程。
符合引用:符合引用字面量形式明确定义在Java虚拟机规范的Class文件格式中。符合引用于虚拟机实现的内存布局无关。

直接引用:直接引用是可以直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。直接引用和虚拟机实习的内存布局相关。如果有了直接引用,目标必定已经存在内存当中。
解析动作主要针对类或接口,字段,类方法, 接口方法的解析。
初始化
类的初始化是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码)。初始化阶段是执行类构造器
()方法的过程。
()方法时由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集顺序是由语句在原文件中出现的顺序决定,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块中可以赋值,但是不能访问。

4.java多线程技术图谱
这里写图片描述

链接:https://www.nowcoder.com/discuss/37315?type=0&order=3&pos=75&page=1
来源:牛客网
1.hashMap原理?插入过程?
2.redis的数据结构?
3.怎么实现LRU结构?
4.单例模式(手写)?
5.内存泄漏原因?
6.数据库索引?优化和缺点?
7.数据库是怎么实现隔离级别的(懵逼)?
8.聚集索引和非聚簇索引额区别?
9.项目中用户权限控制?
二面
1.redis的复制?
2.redis提供分布式服务的方案?服务端,客户端各是怎实现的(搞不明白)?
3. synchronized和lock的区别?原理?介绍一下乐观锁和悲观锁?乐观锁在项目中怎么使用的?
4.hashMap和currentHashMap的区别?CSA?
5.内存泄漏?怎么查询(脑袋一热说没遇见过)?
6.服务上线后要关注服务器的什么指标?
6.介绍一下jvm内存模型?怎么设置Eden,Survivor 的比例?新生代转换成老年代的时间?CMS垃圾回收机制?G1垃圾回收机制?CMS和G1的区别?G1原理?
7.voilate简单介绍下?禁止指令重排序怎么实现?happens-before(表示没听过)?
8.arp协议详细介绍下?
9.数组中找出所有重复的数字?空间复杂度为0(1),时间复杂度最下?
写了个快排,加循环面试官不满意?求答案?
10.树交换左右字数?
11.一个二维数组只含有0,1;将1围成的矩阵中所有0的数字转换成1(不会)?
12.树桩存水问题?例如一维数组412313可以存储水量为5,12341不能存水,313存水2?
只知道思路,渣渣不会写。

链接:https://www.nowcoder.com/discuss/37283?type=0&order=3&pos=104&page=1
来源:牛客网
一面:
1.自我介绍
2.代码:2个单向第一个公共节点
3.linux熟悉吗,怎么杀死进程,怎么批量杀死进程,看进程号,
4.怎么看文件夹下有多少文件
5.数据库,求最高分,max,每个人的最高分max,grou by
6.测试app登陆界面,两个框一个按钮
7.git熟悉吗,怎么创建分支
8.快排归并冒泡二分排序那些复杂度最高

二面:
1.写代码:判断ip是不是合法
2.python垃圾回收机制
3.一个url输入之后用到哪些协议
4.进程线程区别,线程有哪些知识,线程有哪些状态
5.介绍下tcp
6.scoket编程和tcp有什么区别
7.http有哪些状态码,介绍下对它的其他了解,能传输哪些类型的文件
8.测试微信点赞功能

链接:https://www.nowcoder.com/discuss/36782
来源:牛客网
一面(顺序可能有适当修改):
1、一个数组,有正有负,把正的移到右边,负的移到左边。
2、判断一个链表是否有环(我回答快慢指针,因此引出下一个问题)
3、假设一个节点为100的环形单链表,你这方法要走多少步判断出有环,99个节点呢?
4、tcp三次握手的过程?
5、进程与线程的区别?
6、说说你了解的http状态码,http请求方法?
7、简单说下银行家算法?
8、项目等相关。
9、实现一个生产者和消费者?
然后让我在房间等会,过了几分钟,二面面试官进来。

二面:
1、dubbo怎么实现异步的?(由于项目使用了)
2、dubbo底层怎么通信的?
3、进程和线程的区别?linux有进程和线程的区分吗?
4、中断和异常是什么?有什么区别?
5、tcp三次握手和四次挥手,两次握手会怎么样?
6、说说arp协议,nat协议,局域网是怎么通信的?
7、ip地址和mac地址区别?
8、虚拟内存和虚拟地址是什么?
9、死锁的必要条件?举一个死锁的例子,怎么避免死锁,怎么解决死锁?
10、hashmap、concurrenthashmap、hashtable?
11、项目,具体就不说了。
12、说下dns的过程?
13、最小堆的定义?最小堆的插入与删除的全过程?
14、红黑树的定义?为什么是平衡树?怎么维护平衡?
15、B+树的定义?为什么要用b+树,而不用平衡二叉树?
16、聚集索引和非聚集索引的区别?
17、synchronized的原理?
18、synchronized和lock的区别?
19、如果美团给了你offer,你实习公司又转正了,你还来吗?
然后带我出去,说等会不在这间房间,然后路上问我实习的部门leader是谁,卧槽,他认识我leader。很是尴尬。等了几分钟,三面面试官带我去另一个地方面试。

三面(最难,问的很深入):
1、排序算法的稳定性是指什么?
2、说说常见排序算法的稳定性?选择排序为什么不稳定?归并为什么是稳定的?
3、讲一下堆排序的全过程(这里面试官对每一个参数都会问清楚)?
4、你知道的设计模式(我回答单例,工厂,观察者,装饰者,因此引出更多问题)?
5、你会的单例(我回答懒汉,恶汉,静态内部类,枚举,双重检查)?
6、说说双重检查,volatile关键字的作用,因此也问了内存可见性和重排序?
7、说说原子类实现原理?(cas)?
8、说说cas需要哪些参数,操作系统对应的命令是什么?为什么能保证原子性?
9、volatile和cas有什么关联?什么时候可以互相替代?(一脸闷逼)
10、观察者模式有什么用?怎么实现的?
11、java类库那个是用装饰者模式(io)?
12、装饰着模式和代理模式有什么区别?(一脸懵逼)
13、给你一个64G的内存,你会怎么设置堆大小?
14、说说cms收集器的过程?
15、平时线上项目出现问题怎么排查?
16、hash冲突有哪些解决方法?简单说下再哈希法?怎么保证多个hash函数不会出现死循环?(很懵逼)
17、类加载机制?怎么破坏单例(这里应该想让我用类加载机制来破坏)?

题目:写一个多线程程序, 要求多个线程分别计算圆周率PI, 然后求不同线程计算结果的平均值?

import java.math.BigDecimal;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

class MyThread implements  Callable<BigDecimal> {
    private int times;
    public MyThread(int times) {
        super();
        this.times = times;
    }
    public MyThread(){

    }

    /**
     * 计算圆周率PI
     */
    @Override
    public BigDecimal call() throws Exception {
        int validNum = 0;
        double x, y;
        for(int i = 0; i < times; i++) {
            x =  Math.random();
            y =  Math.random();
            if(x*x + y*y < 1.0) validNum++;
        }
        BigDecimal bigTotal = new BigDecimal(times);
        BigDecimal bigValidNum = new BigDecimal(validNum);
        final BigDecimal FOUR = new BigDecimal(4);
        BigDecimal result = bigValidNum.divide(bigTotal, 20, BigDecimal.ROUND_HALF_UP);
        return result.multiply(FOUR);
    }
}

public class TestDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        Scanner scanner = new Scanner(System.in);
        int times = scanner.nextInt();
        FutureTask<BigDecimal> futureTask1 = new FutureTask<BigDecimal>(new MyThread(times));
        FutureTask<BigDecimal> futureTask2 = new FutureTask<BigDecimal>(new MyThread(times));
        FutureTask<BigDecimal> futureTask3 = new FutureTask<BigDecimal>(new MyThread(times));
        FutureTask<BigDecimal> futureTask4 = new FutureTask<BigDecimal>(new MyThread(times));
        FutureTask<BigDecimal> futureTask5 = new FutureTask<BigDecimal>(new MyThread(times));
        ExecutorService excutor = Executors.newFixedThreadPool(5);
        excutor.submit(futureTask1);
        excutor.submit(futureTask2);
        excutor.submit(futureTask3);
        excutor.submit(futureTask4);
        excutor.submit(futureTask5);
        BigDecimal []res = new BigDecimal[5];
        res[0] = futureTask1.get();
        res[1] = futureTask2.get();
        res[2] = futureTask3.get();
        res[3] = futureTask4.get();
        res[4] = futureTask5.get();

        BigDecimal total = new BigDecimal(0);
        for(int i = 0; i < 5; i++) {
            total = total.add(res[i]);
            System.out.println(i + " " + res[i]);
        }
        final BigDecimal FIVE = new BigDecimal(5);
        BigDecimal last = total.divide(FIVE, 20, BigDecimal.ROUND_HALF_UP);
        System.out.println(last);
        excutor.shutdown();
    }
}

问题的描述
启动3个线程打印递增的数字, 线程1先打印1,2,3,4,5, 然后是线程2打印6,7,8,9,10, 然后是线程3打印11,12,13,14,15. 接着再由线程1打印16,17,18,19,20….以此类推, 直到打印到75. 程序的输出结果应该为:

线程1: 1
线程1: 2
线程1: 3
线程1: 4
线程1: 5

线程2: 6
线程2: 7
线程2: 8
线程2: 9
线程2: 10

线程3: 71
线程3: 72
线程3: 73
线程3: 74
线程3: 75
此题参考:http://coolxing.iteye.com/blog/1236696

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class NumberPrint implements Runnable {
    private int state = 1;
    private int n = 1;
    // 使用lock做锁
    private ReentrantLock lock = new ReentrantLock();
    // 获得lock锁的3个分支条件
    private Condition c1 = lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3 = lock.newCondition();

    @Override
    public void run() {
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        // 线程1获得lock锁后, 其他线程将无法进入需要lock锁的代码块.
                        // 在lock.lock()和lock.unlock()之间的代码相当于使用了synchronized(lock){}
                        lock.lock();
                        while (state != 1)
                            try {
                                // 线程1竞争到了lock, 但是发现state不为1, 说明此时还未轮到线程1打印.
                                // 因此线程1将在c1上wait
                                c1.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        // 如果线程1竞争到了lock, 也通过了state判定, 将执行打印任务
                        for (int j = 0; j < 5; j++) {
                            System.out.println(Thread.currentThread().getName()
                                    + ": " + n++);
                        }
                        System.out.println();
                        // 打印完成后将state赋值为2, 表示下一次的打印任务将由线程2执行
                        state = 2;
                        // 唤醒在c2分支上wait的线程2
                        c2.signal();
                    } finally {
                        // 打印任务执行完成后需要确保锁被释放, 因此将释放锁的代码放在finally中
                        lock.unlock();
                    }
                }
            }
        }, "线程1").start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        lock.lock();
                        while (state != 2)
                            try {
                                c2.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        for (int j = 0; j < 5; j++) {
                            System.out.println(Thread.currentThread().getName()
                                    + ": " + n++);
                        }
                        System.out.println();
                        state = 3;
                        c3.signal();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }, "线程2").start();

        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {

                        lock.lock();
                        while (state != 3)
                            try {
                                c3.await();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        for (int j = 0; j < 5; j++) {
                            System.out.println(Thread.currentThread().getName()
                                    + ": " + n++);
                        }
                        System.out.println();
                        state = 1;
                        c1.signal();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }, "线程3").start();
    }
    public static void main(String[] args) {
        new NumberPrint().run();
    }
}
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值