进程和线程基础知识必看(常见面试题)

1、线程和进程

 线程:线程是进程的一个实体,是CPU调度和分派的基本单元。
进程:进程是具有一定独立功能的程序,它是系统进程资源分配和调度的一个独立单元。
区别:
     (1)一个线程只属于一个进程,一个进程包含一个或者多个线程。
     (2)进程拥有独立的内存单元,而多个线程共享内存。
     (3)进程的创建调用fork或者vfork,而线程的创建调用pthead_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束。
     (4)线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的。
     (5)线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的资源。

2、死锁?死锁产生的原因?死锁的必要条件?怎么处理死锁?

  死锁:死锁是指两个或者两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。
  死锁原因:系统资源不足、相互竞争资源。请求资源顺序不当
  死锁的必要条件:
         1.互斥条件:一个资源每次只能被一个进程使用。
         2.请求和保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
         3.不可剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺,只能在进程使用完时由自己释放。
         4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
  避免死锁的方法:
         因为互斥是不可改变的,所以只能破坏其他三个条件中的一个来解除死锁,方法:剥夺资源、杀死其中一个线程。
        避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序做操作来避免死锁。

3、如何在Java中实现线程?
(1)继承Thread类
(2)实现Runnable接口
(3)实现Callable接口通过FutureTask包装器来创建Thread线程
(4)使用ExecutorService、Callable、Future实现有返回结果的多线程

4、用Runnable还是Thread?
Java不支持类的多重继承,但允许你调用多个接口(当然是调用Runnable接口更好)

5、Thread类中start()和run()方法有什么区别?
(1)start()方法被用来启动新创建的线程,而start()内部调用了run()方法。
(2)直接调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动。

6、Java中Runnable和Callable有什么不同?
主要的区别是Callable的call()方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能

7、Java中的volatile变量是什么?
volatile关键字的作用是保证变量在多线程之间的可见性(可见性是说每个线程访问用volatile修饰变量时,volatile都保证线程能得到当前最新的值)
volatile是java中提供的最轻量的同步机制

8、什么是线程安全?Vector是一个线程安全类吗?
线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,
而且其他的变量的值也和预期的一样,就是线程安全的。
Vector是用同步方法来实现线程安全的,而和它相似的ArrayList不是线程安全的。

9、Java中如何停止一个线程?
(1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
(2)使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
(3)使用interrupt方法中断线程。

10、Java中notify和notifyAll有什么区别?
notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保至少有一个线程能继续运行。

11、为什么wait、notify和notifyAll这些方法不在thread类里面?
因为wait、notify和notifyAll都是锁级别的操作,所以把它们定义在Object类中是因为锁属于对象。如果定义在Thread类中,线程正在执行的是哪个锁就不明显了。

12、什么是TreadLocal变量?
ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。

13、为什么wait和notify方法要在同步块中调用?
主要是因为Java API强制要求这样做,如果你不这样做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。

14、什么是线程池?为什么要使用它?
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在初始化一个多线程应用程序过程中创建一个线程集合,
然后在需要执行新的任务时重用这些线程而不是新建一个线程,它们被称为线程池,里面的线程叫工作线程。

    为什么需要线程池?
  (1)线程池改进了一个应用程序的响应时间。
  (2)线程池节省了CLR为每个短生存周期任务创建一个完整的线程的开销并可以在任务完成后回收资源。
  (3)线程池根据当前在系统中运行的进程来优化线程时间片。
  (4)线程池允许我们开启多个任务而不用为每个线程设置属性。
  (5)线程池可以用来解决处理一个特定请求最大线程数量限制问题。

15、怎样检测一个线程是否拥有锁?
在java.lang.Thread中有一个方法叫holdslock(),如果当前线程拥有某个具体对象的锁就返回true。

16、JVM中哪个参数是用来控制线程的堆栈大小的
-Xss参数用来控制线程的堆栈大小。

17、Java中synchronized和ReentrantLock有什么不同?
相同点:
它们都是加锁方式同步,而且都是阻塞式的同步,也就是说如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待。
不同点:
1.对于synchronized来说,它是java的关键字,是原生语法层面的互斥,需要jvm实现。
2.ReentrantLock是JDK1.5之后提供的PI层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。

          Synchronized通过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。
    如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。
    如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

    ReentrantLock类提供了一些高级功能,主要有以下3项:
           1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。
           2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。
           3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

18、有三个线程T1,T2,T3,怎么确保它们按顺序执行?
T3先执行,在T3的run中,调用t2.join,让t2执行完成后再执行t3,在T2的run中,调用t1.join,让t1执行完成后再让T2执行
(join()方法是等待本线程结束后,下一个线程才可以运行)

19、Thread类中的yield方法有什么作用?
yield方法的作用就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行。

转载博客园链接进程和线程常见面试题

你可能感兴趣的:(进程和线程基础知识必看(常见面试题))