Java多线程面试题大全

以下这些面试题是我对着多线程的知识点自己一点点总结下来的,如果有不全的地方欢迎大家留言一起探讨。

多线程和并发、并行的区别:

多线程:是指这个程序运行时产生了不止一个线程

并行:多个CPU同时处理一段逻辑

并发:通过cpu调度算法,让用户觉得是在同时进行,但cpu内部并不是真正的同时

多线程是实现并发机制的一种有效手段

多线程有哪些状态:

1、新建:在new Thread时,jvm会像普通对象一样给线程分配内存,并初始化其成员变量的值

2、就绪:当执行了start方法后,该线程便处于就绪状态,但是它并没有运行,只是表示可以运行了。(注意,当线程被阻塞后重新恢复后,必须先经过就绪状态)

3、运行:如果就绪状态的线程获得了CPU,那么执行run方法后,该线程会处于运行状态,

如果计算机只有一个CPU,那么在任何时刻,只有一个线程处于运行状态。在多处理器的机器上,会有多个线程并行执行。当线程数大于CPU数时,依然会有多个线程在同一CPU上轮换(即并发)的现象。

4、阻塞:可以理解为暂停执行该线程

5、死亡:run或call执行完成后;抛出未捕获的异常后;直接调用stop方法后,线程处于死亡状态

有哪些方法可以让线程进入阻塞状态,然后又如何恢复线程到运行状态

使线程进入阻塞状态的方法:

1、调用sleep方法主动放弃所占用的处理器资源

2、调用了一个阻塞式的IO方法:如等待某个输入输出流的完成

3、线程试图得到一个锁,而该锁正在被其他线程调用

4、线程在等待某个通知(notify)

5、调用suspend方法暂停了线程,暂停后的线程必须通过resume方法来恢复,容易造成死锁,一般不用

恢复线程到运行状态:

1、sleep方法的线程经过了指定的时间

2、阻塞式的IO方法已经返回

3、成功的获取到了试图得到的锁

4、线程正在等等某个通知时,其他线程发出了一个通知

5、调用了resume方法

如何判断线程是否死亡:

可以调用线程对象的isAlive方法,当处于就绪、运行、阻塞时,返回true。当处于新建或死亡时,返回false

能不能对一个死亡的线程重新调用start方法让它重新启动:

不能,死亡就是死亡,该线程将不可再次作为线程执行

就绪状态的线程如何获取处理器资源:

线程从就绪状态到运行状态不受程序控制,而是根据系统线程调用所决定,当处于就绪状态的线程获得到处理器资源时,会进入运行状态。而当处于运行状态的线程失去处理器资源时,会处于就绪状态。但是yield方法可以使线程从运行状态转入就绪状态

Java程序每次运行至少启动几个线程

两个线程,一个是主线程,一个是垃圾回收机制的线程

如果没有启动start而是直接启动线程的run方法会怎么样:

虚拟机会把这个线程当做一个普通的对象,而run方法也会被看做是一个普通的对象中的方法

Join()在多线程中的作用:

让一个线程等待另一个线程完成,比如:我创建了一个MyThread线程:

MyThread myThread = new MyThread();

然后在main()方法中调用(本质是在main线程中调用)myThread.join()方法,那么main线程会进入阻塞状态直到MyThread线程中的run方法的执行完毕后才会继续执行。

什么是后台线程:

有一种线程,是在后台运行的,它的任务是为其他线程提供服务,又称为“守护线程”,

比如JVM的垃圾回收线程就是一个后台线程。后台线程有一个特征,就是如果所有前台线程全部死亡,那么后台线程会自动死亡。

线程睡眠Sleep(long millis):

可以让线程暂停一段时间,当线程进入睡眠状态后,该线程不会获得执行机会,即使系统中没有其他可以执行的线程,处于sleep中的线程也不会执行,直到睡眠结束

sleep()和wait()的区别:

最大的区别是,sleep()在睡眠后不会释放掉锁,而wait()在睡眠后会释放掉锁。

线程让步yield():

将当前正在执行的线程暂停,但是是将线程进入到就绪状态,释放CPU资源

线程优先级是什么,如何改变线程优先级:

每个线程都有一定的优先级,优先级高的线程会获得到更多的执行机会。每个线程的优先级和创建它的父线程的优先级相同,Thread提供了setPriority方法来设定优先级,参数范围为1-10

什么是线程安全,什么是线程不安全(synchronized的作用或者由来)

线程的run方法不具有同步安全性,当多个线程同时处理一个共享数据时,可能导致数据混乱,这就是线程不安全。所以引入了synchronized来解决此问题。被synchronized锁定的同步代码块,只能同时被一个线程获取到,当该线程执行完此方法后,会释放掉该锁,这时其他线程才可以继续获取该锁。Synchronized()括号内可以传任何参数,即Obj类型,但通常是被共同访问的共享资源来作为同步监视器

同步方法是什么,如何使用:

被synchronized修饰的方法,成为同步方法,如:

Public synchronized void getPerson(){};

当内部类中的代码块被synchronized(this)修饰时,请问这个this指的是内部类还是其父类:

内部类

当一个线程访问某个类中的被synchronized修饰的方法时,另一个线程可以访问该类中其他没有被synchronized修饰的方法吗:

可以

当一个线程访问某个类中的被synchronized修饰的方法时,另一个线程可以访问该类中其他被synchronized修饰的方法吗:

不可以,因为Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。

Synchronized如何被释放:

1、当前线程的同步方法,同步代码块执行结束

2、出现了未处理的Error或Exception,导致代码块结束

3、当线程正在执行加锁的代码块或同步方法时,调用了wait()方法,那么会使线程进入阻塞状态,并且释放锁

4、注意:当线程正在执行加锁的代码块或同步方法时,调用sleep()方法或者yield()方法,不会释放锁

什么是死锁,什么情况下会出现死锁:

当所有线程处于阻塞状态,整个程序没有发生异常,也不会有任何提示,就进入了死锁状态;

当两个线程相互等待对方释放锁时,就会发生死锁。

什么是Lock锁,和synchronized有什么区别:

1、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

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

3、Lock可以通过tryLock()方法知道是否成功获取到锁,但是synchronized不行

4、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

5、Lock可以提高多个线程进行读操作的效率(读写锁)。

6、Lock可以实现公平锁,Synchronized不保证公平性。

线程间通信:

wait()/notify()机制:

ThreadA处于阻塞状态(wait方法),ThreadB在run方法中对某个数据做操作,当数据达到我设定的某个条件时,通过notify()来唤醒ThreadA。

好处:提高了CPU的利用率

缺点:如果通知过早,会打乱执行的逻

其他通信方法请参考:

http://www.importnew.com/26850.html

线程池:

为了避免重复创建线程,线程池可以的出现可以让线程进行复用,当需要时,从线程池中取出一个线程,当工作完成后,并不是直接关闭线程,而是将线程归还给线程池供其他任务使用。

常用的线程池:

newFixedThreadPool

固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。

newSingleThreadExecutor

单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。

newCachedThreadPool

缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,核心池最大为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列,他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。

newScheduledThreadPool

定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。

锁池和等待池:

1、锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

2、等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

notify和notifyAll的区别

1、如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

2、当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

3、优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁

你可能感兴趣的:(Java多线程面试题大全)