面试(四)
Java中如何获取到线程dump文件
死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:
(1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java
(2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid
如何在两个线程之间共享数据
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的
sleep方法和wait方法有什么区别
sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器
为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用
这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁
wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别
区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。
为什么要使用线程池
避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目
怎么检测一个线程是否持有对象监视器
Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着"某条线程"指的是当前线程。
怎么唤醒一个阻塞的线程
如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。
什么是多线程的上下文切换
多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。
Java中用到的线程调度算法是什么
抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。
单例模式的线程安全性
单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法,我总结一下:
(1)饿汉式单例模式的写法:线程安全
(2)懒汉式单例模式的写法:非线程安全
(3)双检锁单例模式的写法:线程安全
Semaphore有什么作用
Semaphore就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。
线程类的构造方法、静态块是被哪个线程调用的
线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。
例如:假设Thread2中new了Thread1,main函数中new了Thread2,那么:
(1)Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2自己调用的
(2)Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1自己调用的
高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?
(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
什么是线程?
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
线程和进程有什么区别?
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。每个线程都拥有单独的栈内存用来存储本地数据。
如何在Java中实现线程?
两种方式:java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。
有哪些不同的线程生命周期?
当我们在Java程序中新建一个线程时,它的状态是New。当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running。其他的线程状态还有Waiting,Blocked 和Dead。
什么是死锁(Deadlock)?如何分析和避免死锁?
-
死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。
-
分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。 避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法。
什么是线程安全?Vector是一个线程安全类吗?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况下也不会出现计算失误。很显然你可以将集合类分成两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它相似的ArrayList不是线程安全的。
常用的linux指令
-
mkdir dir1 创建一个叫做 'dir1' 的目录'
-
mkdir dir1 dir2 同时创建两个目录
-
cd .. 返回上一级目录
-
cd ../.. 返回上两级目录
-
shutdown -r now 重启(1)
-
shutdown -h now 关闭系统(1)
-
uname -m 显示机器的处理器架构(2)
-
find / -user user1 搜索属于用户 'user1' 的文件和目录
-
当使用RMI技术实现远程方法调用时,Mic能为远程对象生成Sub和Skeleton命令
-
kill命令,常用于杀死进程;
-
tar命令,tar命令是Unix/Linux系统中备份文件的可靠方法,几乎可以工作于任何环境中,它的使用权限是所有用户
-
在/etc/fstab 文件中指定的文件系统加载参数中, noauto 参数一般用于CD-ROM 等移动设备。
-
在使用mkdir 命令创建新的目录时,在其父目录不存在时先创建父目录:-p
-
uncompress,可以用来解压缩的命令
-
具有很多C 语言的功能,又称过滤器的是:awk
-
一台主机要实现通过局域网与另一个局域网通信,需要做的工作是 :定义一条本机指向所在网络网关的路由
-
建立动态路由需要用到的文件有:/etc/gateways
-
在局域网络内的某台主机用ping 命令测试网络连接时发现网络内部的主机都可以连同,而不能与公网连通,问题可能是:局域网的网关或主机的网关设置有误
-
在shell 中变量的赋值有四种方法,其中,采用name=12 的方法称:直接赋值
-
cut命令可以从文本文件的每一行中截取指定内容的数据。
-
若一台计算机的内存为128MB,则交换分区的大小通常是256MB
-
在安装Linux 的过程中的第五步是让用户选择安装方式,如果用户希望安装部分组件(软件程序),并在选择好后让系统自动安装,则用menu指令
-
Linux 有三个查看文件的命令,若希望在查看文件内容过程中可以用光标上下移动来查看文件内容,应使用less命令
-
FTP能够实现本地与远程主机之间的文件传输工作
-
traceroute:当我们与某远程网络连接不上时,就需要跟踪路由查看,以便了解在网络的什么位置出现了问题
-
DNS 域名系统主要负责主机名和IP地址之间的解析。
-
Linux 系统通过write命令给其他用户发消息。
-
NFS 是网络文件系统。
-
cpio:在Linux 的安全系统中完成文件向磁带备份的工作
-
外部设备文件,一般应将其放在/dev目录中
-
shutdown –r now:在重新启动Linux 系统的同时把内存中的信息写入硬盘
-
网络管理具备以下几大功能:配置管理、故障管理、性能管理、安全管理和计费管理等
-
halt:关闭linux 系统(不重新启动)
-
arp:实现从IP 地址到以太网MAC 地址转换
-
在vi 编辑器中的命令模式下,键入< o>可在光标当前所在行下添加一新行
-
在vi 编辑器中的命令模式下,删除当前光标处的字符使用< x>命令
-
在vi 编辑器中的命令模式下,重复上一次对编辑的文本进行的操作,可使用< .>命令
-
traceroute:删除文件
-
exit:退出交互模式的shell
Java异常处理try-catch-finally的执行过程
1)程序首先执行可能发生异常的try语句块。
2)如果try语句没有出现异常则执行完后跳至finally语句块执行;
3)如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。
4)catch语句块可以有多个,分别捕获不同类型的异常。
5)catch语句块执行完后程序会继续执行finally语句块。
finally语句是可选的,如果有的话,则不管是否发生异常,finally语句都会被执行。需要注意的是即使try和catch块中存在return语句,finally语句也会执行,是在执行完finally语句后再通过return退出。
-
NullPointerException空指针异常
-
ClassCastException类型转换异常
-
IndexOutOfBoundsException索引超出边界的异常
以上这些异常都是程序在运行时发生的异常,所以不需要在编写程序时声明
-
FileNotFoundException:要在编写程序时声明
JAVA性能优化
真正影响JAVA程序性能的,就是碎片化。碎片是JAVA堆内存中的空闲空间,可能是TLAB剩余空间,也可能是被释放掉的具有较长生命周期的小对象占用的空间。
-
减少new对象。每次new对象之后,都要开辟新的内存空间。这些对象不被引用之后,还要回收掉。因此,如果最大限度地合理重用对象,或者使用基本数据类型替代对象,都有助于节省内存;
-
多使用局部变量,减少使用静态变量。局部变量被创建在栈中,存取速度快。静态变量则是在堆内存;
-
避免使用finalize,该方法会给GC增添很大的负担;
-
如果是单线程,尽量使用非多线程安全的,因为线程安全来自于同步机制,同步机制会降低性能。例如,单线程程序,能使用HashMap,就不要用HashTable。同理,尽量减少使用synchronized
-
用移位符号替代乘除号。eg:a*8应该写作a<<3
-
对于经常反复使用的对象使用缓存;
-
尽量使用基本类型而不是包装类型,尽量使用一维数组而不是二维数组;
-
尽量使用final修饰符,final表示不可修改,访问效率高
-
单线程情况下(或者是针对于局部变量),字符串尽量使用StringBuilder,比StringBuffer要快;
-
尽量早释放无用对象的引用 大部分时,方法局部引用变量所引用的对象会随着方法结束而变成垃圾,因此,大部分时候程序无需将局部,引用变量显式设为null。
-
尽量避免使用二维数组 二维数据占用的内存空间比一维数组多得多,大概10倍以上。
-
ArrayList & LinkedList 一个是线性表,一个是链表,一句话,随机查询尽量使用ArrayList,ArrayList优于LinkedList,LinkedList还要移动指针,添加删除的操作LinkedList优于ArrayList,ArrayList还要移动数据,不过这是理论性分析,事实未必如此,重要的是理解好2者得数据结构,对症下药。
-
单线程应尽量使用 HashMap, ArrayList,除非必要,否则不推荐使用HashTable,Vector,它们使用了同步机制,而降低了性能。
java数组有什么特征
-
数组会在内存中开辟一块连续的空间,每个空间相当于之前的一个变量,称为数组的元素element
-
索引从0开始
-
数组元素有序的,不是大小顺序,是索引 的顺序
-
数组中可以存储基本数据类型,可以存储引用数据类型;但是对于一个数组而言,数组的类型是固定的,只能是一个length:数组的长度,数组的长度是固定的,一经定义,不能再发生变化
Java的HashMap和Hashtable有什么区别?HashSet和HashMap有什么区别?
HashMap与Hashtable实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
两者的主要区别如下
1、Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口
2、Hashtable继承Dictionary类,HashMap实现Map接口
3、Hashtable线程安全,HashMap线程非安全
4、Hashtable不允许null值,HashMap允许null值
HashSet与HashMap的区别
1、HashSet底层是采用HashMap实现的。HashSet 的实现比较简单,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
2、HashMap的key就是放进HashSet中对象,value是Object类型的。
3、当调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量
Java的安全性如何理解
-
Java取消了强大但又危险的指针,而代之以引用。由于指针可进行移动运算,指针可随便指向一个内存区域,而不管这个区域是否可用,这样做是危险的,因为原来这个内存地址可能存储着重要数据或者是其他程序运行所占用的,并且使用指针也容易数组越界。
-
垃圾回收机制:不需要程序员直接控制内存回收,由垃圾回收器在后台自动回收不再使用的内存。避免程序忘记及时回收,导致内存泄露。避免程序错误回收程序核心类库的内存,导致系统崩溃。
-
异常处理机制:Java异常机制主要依赖于try、catch、finally、throw、throws五个关键字。
-
强制类型转换:只有在满足强制转换规则的情况下才能强转成功。
Java的序列化是什么,如何实现Java的序列化?列举在哪些程序中见过Java序列化?
-
ava中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个字节流中使其可以通过socket进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来重构一个相同的对象。一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。
- 对象的序列化主要有两种用途:
- 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
- 在网络上传送对象的字节序列。
Java的类加载器都有哪些,每个类加载器都有加载哪些类,什么是双亲委派模型,是做什么的
类加载器按照层次,从顶层到底层,分为以下三种:
(1)启动类加载器(Bootstrap ClassLoader)
这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
(2)扩展类加载器(Extension ClassLoader)
这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
(3)应用程序类加载器(Application ClassLoader)
这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器
双亲委派模型:
双亲委派模型要求除了顶层的启动类加载器外,其他的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承关系来实现,而是都使用组合关系来复用父加载器的代码。
工作过程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
好处:
Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类,判断两个类是否相同是通过classloader.class这种方式进行的,所以哪怕是同一个class文件如果被两个classloader加载,那么他们也是不同的类。
已知表达式int m[] = {0,1,2,3,4,5,6}; 下面那个表达式的值与数组的长度相等()
A m.length() B. m.length C. m.length()+1 D. m.length+1 答案:B 分析:数组的长度是.length
提供Java存取数据库能力的包是()
A java.sql B. java.awt C. java.lang D. java.swing
答案:A
分析: java.awt和javax.swing两个包是图形用户界面编程所需要的包; java.lang包则提供了Java编程中用到的基础类。
执行如下程序代码,c的值打印出来是()
public class Test1 { public static void main(String[] args) { int a = 0; int c = 0; do{ --c; a = a - 1; } while (a > 0); System.out.println(c); } }
A 0 B. 1 C. -1 D. 死循环
答案:C
分析: do-while循环的特点是先执行后判断,所以代码先执行--c操作,得到c为-1,之后执行a=a-1的操作,得到a为-1,然后判断a是否大于0,判断条件不成立,退出循环,输出c为-1。
下列哪一种叙述是正确的()
A abstract修饰符可修饰字段,方法和类
B. 抽象方法的body部分必须用一对大括号{}包住
C. 声明抽象方法,大括号可有可无
D. 声明抽象方法不可写出大括号
答案:D
分析: abstract只能修饰方法和类,不能修饰字段; 抽象方法不能有方法体,即没有{}; 同B。
下列哪种说法是正确的()
A 实例方法可直接调用超类的实例方法
B. 实例方法可直接调用超类的类方法
C. 实例方法可直接调用其他类的实例方法
D. 实例方法可直接调用本类的类方法
答案:D
分析: 实例方法不可直接调用超类的私有实例方法; 实例方法不可直接调用超类的私有的类方法; 要看访问权限。
Java程序的种类有()(多选)
A 类 (Class)
B. Applet
C. Application
D. Servlet
答案:BCD
分析: 是Java中的类,不是程序; 内嵌于Web文件中,由浏览器来观看的Applet; 可独立运行的 Application; 服务器端的 Servlet。
下列说法正确的有()(多选)
A 环境变量可在编译source code时指定
B. 在編译程序时,所指定的环境变置不包括class path
C. javac —次可同时编译数个Java 源文件
D. javac.exe能指定编译结果要置于哪个目录(directory)
答案:BCD
分析: 环境变量一般都是先配置好再编译源文件。
下列说法错误的有()(多选)
A 在类方法中可用this来调用本类的类办法
B. 在类方法中调用本类的类方法时可以直接调用
C. 在类方法中只能调用本类中的类方法
D. 在类方法中绝对不能调用实例方法
答案:ACD
分析: 类方法是在类加载时被加载到方法区存储的,此时还没有创建对象,所以不能使用this或者super关键字;
C. 在类方法中还可以调用其他类的类方法; D. 在类方法可以通过创建对象来调用实例方法。
-
能够修饰interface的只有public、abstract以及默认的三种修饰符。
-
Java类可以作为类型定义机制和数据封装机制
在调用方法时,若要使方法改变实参的值,可以?()
A 用基本数据类型作为参数
B.用对象作为参数
C.A和B都对
D.A和B都不对
答案:B
分析:基本数据类型不能改变实参的值
-
可移植反映了java程序并行机制
若需要定义一个类域或类方法,应使用哪种修饰符?()
A static
B.package
C.private
D.public
答案:A
以InputStream(输入流)/OutputStream(输出流)为后缀的是字节流;
以Reader(输入流)/Writer(输出流)为后缀的是字符流。
java.ByteArrayOutputStream:将信息写入内存的类
Java创建对象的几种方式(重要):
(1) 用new语句创建对象,这是最常见的创建对象的方法。
(2) 运用反射手段,调用java.lang.Class或者 java.lang.reflect.Constructor类的newInstance()实例方法。
(3) 调用对象的clone()方法
(4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法。
注意:(1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不会调用构造函数 (4)是从文件中还原类的对象,也不会调用构造函数。
Math.round(-11.2)的运行结果是。
答案: -11
分析:
小数点后第一位=5
正数:Math.round(11.5)=12
负数:Math.round(-11.5)=-11
小数点后第一位<5
正数:Math.round(11.46)=11
负数:Math.round(-11.46)=-11 小
数点后第一位>5
正数:Math.round(11.68)=12
负数:Math.round(-11.68)=-12
根据上面例子的运行结果,我们还可以按照如下方式总结,或许更加容易记忆:
参数的小数点后第一位<5,运算结果为参数整数部分。
参数的小数点后第一位>5,运算结果为参数整数部分绝对值+1,符号(即正负)不变。
参数的小数点后第一位=5,正数运算结果为整数部分+1,负数运算结果为整数部分。
总结:大于五全部加,等于五正数加,小于五全不加。
Java线程的优先级设置遵循什么原则?
(1) 线程创建时,子线程继承父线程的优先级
(2) 线程创建后,可在程序中通过调用setPriority( )方法改变线程的优先级
(3) 线程的优先级是1~10之间的正整数,数字越大优先级越高,默认的优先级是居中,即为5。
java集合和数组的比较(为什么引入集合)
数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下:
1)数组的效率高于集合类.
2)数组能存放基本数据类型和对象,而集合类中只能放对象。
3)数组容量固定且无法动态改变,集合类容量动态改变。
4)数组无法判断其中实际存有多少元素,length只告诉了array的容量。
5)集合有多种实现方式和不同的适用场合,而不像数组仅采用顺序表方式。
6)集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开发效率。
Java集合类框架的基本接口有哪些?
-
Collection:代表一组对象,每一个对象都是它的子元素。
-
Set:不包含重复元素的Collection。
-
List:有顺序的Collection,并且可以包含重复元素。
-
Map:可以把键(key)映射到值(value)的对象,键不能重复。
ArrayList list=new ArrayList(20);中的list扩充几次()
A. 0 B. 1 C. 2 D. 3
答案:A 分析:已经指定了长度, 所以不扩容
以下结构中,哪个具有同步功能()
A. HashMap B. ConcurrentHashMap C. WeakHashMap D. TreeMap
答案:B
分析: A,C,D都线程不安全,B线程安全,具有同步功能