某群入群面试题

1,什么是线程安全 (参考书:https://book.douban.com/subject/10484692/
2,都说String是不可变的,为什么我可以这样做呢
String a = "1";
a = "2";
3,HashMap的实现原理
4,写出三种单例模式,如果能考虑线程安全最好
5,ArrayList和LinkedList有什么区别
6,实现线程的2种方式
7,JVM的内存结构
8,Lock与Synchronized的区别
9,数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么。
10,请解释如下jvm参数的含义:
-server -Xms512m -Xmx512m -Xss1024K
-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。

1、什么是线程安全?

从java的角度看,当多个线程同时访问某个类时,不管运行时环境采用何种调用方式或者这些线程将如何交替执行,主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

2、都说String是不可变的,为什么我可以这样做呢? String a = "1";a = "2";

String不可变指的是String类和其内部的char型数组用final修饰的,这个两个效果,一、String类用final修饰,不能被继承;二、char型数组用final修饰,其指针不得修改指向的堆地址,但是在堆内部的数组里面时可以变换数值的。String a=”1”;表示初始化一个String赋值为”1”,而s只是一个String对象的引用,指向了刚才这个对象。而 a=”2”;不是在原内存地址上修改数据,而是重新创建了一个新的对象,并将该引用指向这个新的对象;而原来的对象还在常量池中,没有消失。

3、HashMap的实现原理

HashMap是基于哈希表的Map接口的非同步(线程不安全)实现。并允许使用null值和null键。下面以JAVA8为例。

一、HashMap数据结构

HashMap的数据结构实际上是一个“链表散列”的数据结构,即数组和链表的结合体(JDK1.8增加了红黑树部分)。HashMap的底层是一个数组结构,数组中的每一项是一个链表。当新建一个HashMap时,会初始化一个Node类型的数组,Node[] table,即哈希桶数组。可选参数为initialCapacity,数组大小,loadFactor,数组被填满程度的最大比例。当数组中的Node的个数(而不是已占用的位置数)大于initialCapacity*loadFactor时就需要扩容,调整数组的大小为当前的2倍。同时,初始化容量的大小也是2的次幂(大于等于设定容量的最小次幂),则数组的大小在扩容前后都将是2的次幂。默认的capacity为16,loadFactor为0.75。

Node是一个static class,其中包含了key和value,也就是键值对,还包含了一个next的Node指针,它持有一个指向下一个元素的引用,这就构成了链表。负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能。

二、HashMap核心方法(源代码太长不放了)

  • 1、hash方法

设计者不假定用户实现了良好的hashCode方法,所以需要对hashCode再计算一次,即在get方法和pu方法计算下标时,先对hashCode进行hash操作,然后再通过hash值进一步hash计算得到数组下标。

  • 2、put()方法

当put的时候,如果key存在了,那么新的value会代替旧的value,并且如果key存在的情况下,该方法返回的是旧的value,如果key不存在,那么返回null。当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,这样插入新entry时不需要遍历链表,时间复杂度为O(1)。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。Java8中对链表长度增加了一个阈值,超过阈值链表将转化为红黑树,查询时间复杂度降为O(logn),提高了链表过长时的性能。

  • 3、get()方法

根据key的hash方法得到数据下标,然后遍历Node对象链表,直到找到元素为止。

  • 4.、resize()方法

这里就是使用一个容量更大的数组(原来的两倍)来代替已有的容量小的数组,然后rehash形成新的数组。因为使用的的是2次幂的扩展(指长度扩为原来2倍),所以,元素的位置要么是在原位置,要么是在原位置再移动2次幂的位置。因此,在扩充HashMap的时候,不需要像JDK1.7的实现那样重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”。

4、三种单例模式,线程安全

5、ArrayList和LinkedList有什么区别?

ArrayList和LinkedList都是实现了List接口的类,他们都是元素的容器,用于存放对象的引用。但是他们有区别的。

ArrayList:内部使用数组的形式实现了存储,实现了RandomAccess接口,利用数组的下面进行元素的访问,因此对元素的随机访问速度非常快。因为是数组,所以ArrayList在初始化的时候,有初始大小10,插入新元素的时候,会判断是否需要扩容,扩容的步长是0.5倍原容量,扩容方式是利用数组的复制,因此有一定的开销;

另外,ArrayList在进行元素插入的时候,需要移动插入位置之后的所有元素,位置越靠前,需要位移的元素越多,开销越大,相反,插入位置越靠后的话,开销就越小了,如果在最后面进行插入,那就不需要进行位移;

LinkedList:内部使用双向链表的结构实现存储,LinkedList有一个内部类作为存放元素的单元,里面有三个属性,用来存放元素本身以及前后2个单元的引用,另外LinkedList内部还有一个header属性,用来标识起始位置,LinkedList的第一个单元和最后一个单元都会指向header,因此形成了一个双向的链表结构。

LinkedList是采用双向链表实现的。所以它也具有链表的特点,每一个元素(结点)的地址不连续,通过引用找到当前结点的上一个结点和下一个结点,即插入和删除效率较高,只需要常数时间,而get和set则较为低效。

LinkedList的方法和使用和ArrayList大致相同,由于LinkedList是链表实现的,所以额外提供了在头部和尾部添加/删除元素的方法,也没有ArrayList扩容的问题。另外,ArrayList和LinkedList都可以实现栈、队列等数据结构,但LinkedList本身实现了队列的接口,所以更推荐用LinkedList来实现队列和栈。

6、实现线程的2种方式

1、继承 Thread 类

继承Thread类,重写run方法,在启动线程的时候,调用了Thread类的start方法。

2、实现 Runnable 接口

实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target。

  • 1、定义一个类实现Runnable接口,作为线程任务类。

  • 2、重写run方法,并实现方法体,方法体的代码就是线程所执行的代码。

  • 3、定义一个可以运行的类,并在main方法中创建线程任务类。

  • 4、创建Thread类,并将线程任务类做为Thread类的构造方法传入。

  • 5、启动线程。

这样可以做到线程任务和线程的控制分离,即解耦。

3、通过线程池创建线程、Executor框架

有返回值,通过Callable接口,实现call方法。执行Callable任务,获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object对象。

7、JVM的内存结构

JVM内存结构主要有5大块。方法区和堆是所有线程共享的内存区域;而Java栈、本地方法栈和程序计数器是运行是线程私有的内存区域。

1、Java堆(Heap)

Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

Java堆是垃圾收集器管理的主要区域,也被称做“GC堆”。如果从内存回收的角度看,现在收集器基本都是采用分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

2、方法区(Method Area)

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

Java虚拟机规范对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样“永久”存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,一般来说这个区域的回收“成绩”比较难以令人满意,尤其是类型的卸载,条件相当苛刻。运行时常量池是方法区的一部分。

根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

3、程序计数器(Program Counter Register)

程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

4、JVM栈(JVM Stacks)

与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

局部变量表存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型。其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot),其余的数据类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈无法申请到足够的内存时会抛出OutOfMemoryError异常。

5、本地方法栈(Native Method Stacks)

本地方法栈(Native Method Stacks)与虚拟机栈非常相似的,其区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,具体的虚拟机可以自由实现它。HotSpot直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

8、Lock与Synchronized的区别

下面说明Lock和Synchronized的区别:

  • 1、synchronized是Java的关键字,可以用来修饰一个方法或者一个代码块,而Lock是一个接口,主要实现是ReentrantLock类。

  • 2、synchronized就不是可中断锁,而Lock是可中断锁。 如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,由于等待时间过长,线程B不想等待,想先处理其他事情,它可以中断自己或者在别的线程中中断它。

  • 3、synchronized是非公平锁,它无法保证等待的线程获取锁的顺序。ReentrantLock,默认情况下是非公平锁,但是可以设置为公平锁。

  • 4、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  • 5、通过Lock可以知道有没有成功获取锁,而synchronized不行。

9、数据库隔离级别有哪些,各自的含义是什么,MYSQL默认的隔离级别是是什么。

数据库的事务隔离级别有四种,分别是,未提交读,提交读,可重复读,串行读。

  • 1、未提交读(Read Uncommitted):允许脏读,可能读取到其他会话中未提交事务修改的数据

  • 2、提交读(Read Committed):一个事务只能看见其他已经提交事务的数据修改。这会导致不可重复读,即在一个事务中,多次读同一数据。在这个事务还没有结束时,另外一个事务访问修改同一数据。在第一个事务中的两次读数据之间,由于第二个事务的修改,第一个事务两次读到的的数据可能是不一样的。

  • 3、可重复读(Repeated Read): 在同一个事务内的查询都是事务开始时刻一致的。它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。但是可能会有幻读,即当一个事务读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当该事务再读取该范围的数据行时,会发现有新的“幻影” 行 。

  • 4、串行读(Serializable) :完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞,从而解决幻读问题。

MYSQL的InnoDB引擎默认是可重复读

10、请解释如下jvm参数的含义:

-server -Xms512m -Xmx512m -Xss1024K

-XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxTenuringThreshold=20 XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly。

-servier 设置jvm为服务器模式,

-Xms512m -Xmx512m -Xss1024K,JAVA堆最小为512M,最大为512M,JVM栈为1024K

-XX:PermSize=256m -XX:MaxPermSize=512m JVM初始分配方法区(非堆)内存为256M,最大为512M(在HotSpot虚拟机为持久代,该参数在java8以后失效)

-XX:MaxTenuringThreshold=20对象在新生代坚持20次Mirror GC后,晋升到老年代。

XX:CMSInitiatingOccupancyFraction=80 设置CMS收集器在老年代空间被占用80%后触发GC。

-XX:+UseCMSInitiatingOccupancyOnly。关闭CMS收集器的动态检测机制。禁止CMS根据自己的预测自动执行垃圾回收。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容

  • 在一个方法内部定义的变量都存储在栈中,当这个函数运行结束后,其对应的栈就会被回收,此时,在其方法体中定义的变量将不...
    Y了个J阅读 4,415评论 1 14
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,230评论 11 349
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,105评论 0 8
  • 1、字符串swif中字符串的类型是String ,用""修饰1-1、字符串的定义: 定义可变字符串: var...
    7dfa9c18c1d1阅读 127评论 0 0
  • 上篇文章我总结了自己碎片化学习了近一年,却依然缺失方向、没有明确目标。是因自己没深入思考过学到的知识是否能构...
    诗苑的成长花园阅读 252评论 1 0