java面试题整理
- 格式:docx
- 大小:491.79 KB
- 文档页数:12
1Java1)进程、线程进程是执行着的应用程序,而线程是进程内部的一个执行序列。
一个进程可以有多个线程。
线程又叫做轻量级进程。
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。
不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。
进程线程的区别:简而言之,一个程序至少有一个进程,一个进程至少有一个线程。
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。
但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。
但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
这就是进程和线程的重要区别。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。
线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
2)java有三种方式可以用来创建线程,java不支持类的多重继承,但允许你调用多个接口1.继承Thread类最简单的new 方法,继承一个线程类2.实现Runnable接口当需要继承其他类的功能时,选runnable接口3.借助框架程序可用Executor框架来创建线程池,线程池可以限制线程的数量并且可以回收再利用这些线程3)概括解释线程在执行过程中存在如下几种状态,注意了解其之间的区别。
就绪(Runnable):线程准备运行,不一定立马就能开始执行。
运行中(Running):进程正在执行线程的代码。
等待中(Waiting): 线程处于阻塞的状态,等待外部的处理结束。
睡眠中(Sleeping):线程被强制睡眠。
I/O阻塞(Blocked on I/O):等待I/O操作完成。
同步阻塞(Blocked on Synchronization):等待获取锁。
死亡(Dead):线程完成了执行。
4)如何创建守护线程?使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。
5)用户线程和守护线程(Daemon线程)有什么区别?当我们在Java程序中创建一个线程,它就被称为用户线程。
一个守护线程是在后台执行并且不会阻止JVM终止的线程。
当没有用户线程在运行的时候,JVM关闭程序并且退出。
一个守护线程创建的子线程依然是守护线程。
守护进程能被jvm终止退出,用户进程不能被jvm终止。
daemon线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。
6)如何确保线程安全?在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile 关键字,使用不变类和线程安全类。
a.同步synchronized同步块大家都比较熟悉,通过synchronized 关键字来实现;所有加上synchronized 的方法和块语句,在多线程访问的时候,同一时刻只能有一个线程能够访问。
b.volatile 关键字volatile 是一个特殊的修饰符,只有成员变量才能使用它。
在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。
volatile 变量可以保证下一个读取操作会在前一个写操作之后发生。
线程都会直接从内存中读取该变量并且不缓存它。
这就确保了线程读取到的变量是同内存中是一致的。
c.ThreadLocal 变量ThreadLocal 是Java里一种特殊的变量。
每个线程都有一个ThreadLocal 就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。
如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。
首先,通过复用减少了代价高昂的对象的创建个数。
其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。
7)线程sleep 和wait 的区别1.sleep指线程进入休眠状态,保持对象锁,仅释放cpu。
wait指线程处于进入等待状态,释放对象锁,释放cpu。
2.sleep(100L)意思为:保持对象锁,线程休眠100毫秒wait(100L)意思为:释放对象锁,线程等待100毫秒。
3.不同在于sleep方法是Thread类中的方法,调用它的时候不会释放锁;wait方法是Object类中的方法,调用它的时候会释放锁。
Java程序中wait 和sleep都会造成某种形式的暂停,它们可以满足不同的需要。
wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。
需要注意的是,sleep()并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。
8) Thread 类中的start() 和run() 方法有什么区别?start()方法被用来启动新创建的线程,使该被创建的线程状态变为可运行状态。
run()方法调用的时候,没有新的线程启动,只会是在原来的线程中调用,这个方法同普通类的run方法一样。
9) Java中Runnable和Callable有什么不同?Runnable:JDK1.0开始就有了,仅是执行Runnable的run()方法没有返回结果。
Callable是在JDK1.5增加的。
不仅执行,Callable的call() 方法可以返回值和抛出异常,且还可以返回装载有计算结果的Future对象。
10)死锁产生原因、必要条件、解决办法产生死锁的原因:一是系统提供的资源数量有限,不能满足每个进程的使用;二是多道程序运行时,进程推进顺序不合理。
产生死锁的四个必要条件:(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决死锁的基本方法:1.预防死锁:资源一次性分配:(破坏请求和保持条件)可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)2.避免死锁:预防死锁的几种策略,会严重地损害系统性能。
因此在避免死锁时,要施加较弱的限制,从而获得较满意的系统性能。
由于在避免死锁的策略中,允许进程动态地申请资源。
因而,系统在进行资源分配之前预先计算资源分配的安全性。
若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。
其中最具有代表性的避免死锁算法是银行家算法。
3.检测死锁首先为每个进程和每个资源指定一个唯一的号码;然后建立资源分配表和进程等待表。
4.解除死锁:当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。
11) String类和StringBuffer类的区别stringbuffer区别于string的地方在于:多用于处理动态的字符,对字符进行增加或删除。
StringBuilder:线程非安全的,速度快,适用于单线程StringBuffer:线程安全的,速度慢,适用于多线程String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全)简要的说,String 类型和StringBuffer 类型的主要性能区别其实在于String 是不可变的对象, 因此在每次对String 类型进行改变的时候其实都等同于生成了一个新的String 对象,然后将指针指向新的String 对象,所以经常改变内容的字符串最好不要用String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM 的GC 就会开始工作,那速度是一定会相当慢的。
而如果是使用StringBuffer 类则结果就不一样了,每次结果都会对StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。
所以在一般情况下我们推荐使用StringBuffer ,特别是字符串对象经常改变的情况下。
而在某些特别情况下,String 对象的字符串拼接其实是被JVM 解释成了StringBuffer 对象的拼接,所以这些时候String 对象的速度并不会比StringBuffer 对象慢,而特别是以下的字符串对象生成中,String 效率是远要比StringBuffer 快的:String S1 = “This is only a” + “ simple” + “ test”;StringBuffer Sb = new StringBuilder(“This is only a”).append(“simple”).append(“test”); 你会很惊讶的发现,生成String S1 对象的速度简直太快了,而这个时候StringBuffer 居然速度上根本一点都不占优势。
其实这是JVM 的一个把戏,在JVM 眼里,这个String S1 = “This is only a”+ “simple”+ “test”; 其实就是:String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。