CPU是电脑的核心所在,如果能提高CPU的运行效率,相应的也能提高一个程序的运行效率。采用多线程的方式就可以提高CPU的使用率,可以同时完成几件事情而互不干扰,在java语言中,学习好多线程无疑是至关重要的,多线程面试题在java程序员的面试中是常出现的,下面总结了一些常考的最新多线程面试题,大家可以一起来学习。
1、说明类java.lang.ThreadLocal的作用和原理是什么?
答:作用:要编写一个多线程安全(Thread-safe)的程序是困难的,为了让线程共享资源,必须小心地对共享资源进行同步,同步带来一定的效能延迟,而另一方面,在处理同步的时候,又要注意对象的锁定与释放,避免产生死结,种种因素都使得编写多线程程序变得困难。尝试从另一个角度来思考多线程共享资源的问题,既然共享资源这么困难,那么就干脆不要共享,何不为每个线程创造一个资源的复本。将每一个线程存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保管该线程所独享的资源。
ThreadLocal的原理:ThreadLocal做到为每一个线程维护变量的副本,实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
2、在java中怎么实现多线程?描述线程状态的变化过程。
答:当多个线程访问同一个数据时,容易出现线程安全问题,需要某种方式来确保资源在某一时刻只被一个线程使用。需要让线程同步,保证数据安全线程同步的实现方案:
同步代码块和同步方法,均需要使用synchronized关键字。
同步代码块:public void makeWithdrawal(int amt) {
synchronized (acct) { }
}
同步方法:public synchronized void makeWithdrawal(int amt) { }
线程同步的好处:解决了线程安全问题;线程同步的缺点:性能下降,可能会带来死锁。
3、乐观锁与悲观锁各是什么?
答:悲观锁(Pessimistic Lock),
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock),
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
4、在多线程编程里,wait方法的调用方式是怎样的?
答:wait方法是线程通信的方法之一,必须用在
synchronized方法或者synchronized代码块中,否则会抛出异常,这就涉及到一个“锁”的概念,而wait方法必须使用上锁的对象来调用,从而持有该对象的锁进入线程等待状态,直到使用该上锁的对象调用notify或者notifyAll方法来唤醒之前进入等待的线程,以释放持有的锁。
5、volatile关键字是否能保证线程安全?
答:不能。虽然volatile提供了同步的机制,但是知识一种弱的同步机制,如需要强线程安全,还需要使用synchronized。Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
6、创建线程的有哪些方式?
答:(1)继承Thread类创建线程类;(2)通过Runnable接口创建线程类;(3)通过Callable和Future创建线程;(4)通过线程池创建。
7、线程池的优点都有什么?
答:(1)重用存在的线程,减少对象创建销毁的开销;(2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞;(3)提供定时执行、定期执行、单线程、并发数控制等功能。
8、synchronized和ReentrantLock的区别?
答:synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:
(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁;
(2)ReentrantLock可以获取各种锁的信息;
(3)ReentrantLock可以灵活地实现多路通知。
9、线程的调度策略都有什么?
答:线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线程的运行:(1)线程体中调用了yield方法让出了对cpu的占用权利;(2)线程体中调用了sleep方法使线程进入睡眠状态;(3)线程由于IO操作受到阻塞;(4)另外一个更高优先级线程出现;(5)在支持时间片的系统中,该线程的时间片用完。
10、死锁的原因?
答:(1)是多个线程涉及到多个锁,这些锁存在着交叉,所以可能会导致了一个锁依赖的闭环。例如:线程在获得了锁A并且没有释放的情况下去申请锁B,这时,另一个线程已经获得了锁B,在释放锁B之前又要先获得锁A,因此闭环发生,陷入死锁循环。
(2)默认的锁申请操作是阻塞的。
所以要避免死锁,就要在一遇到多个对象锁交叉的情况,就要仔细审查这几个对象的类中的所有方法,是否存在着导致锁依赖的环路的可能性。总之是尽量避免在一个同步方法中调用其它对象的延时方法和同步方法。
11、怎么唤醒一个阻塞的线程?
答:如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。
12、不可变对象对多线程有什么帮助?
答:不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。
13、什么是多线程的上下文切换?
答:多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。
14、同步和异步有何异同?
答:(1)如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
(2)当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
15、Java线程数过多会造成什么异常?
答:(1)线程的生命周期开销非常高;(2)消耗过多的CPU资源。如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置。大量空闲的线程会占用许多内存,给垃圾回收器带来压力,而且大量的线程在竞争CPU资源时还将产生其他性能的开销;(3)降低稳定性。JVM在可创建线程的数量上存在一个限制,这个限制值将随着平台的不同而不同,并且承受着多个因素制约,包括JVM的启动参数、Thread构造函数中请求栈的大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么可能抛出OutOfMemoryError异常。
以上是今天整理的java多线程面试题的内容,大家可以根据给出相应的参考答案来学习。CPU要花同样多的时间去完成所有的事情,但多线程可以让CPU掺插地同时做多件事情,希望朋友们可以通过多线程编程教程来学习更多的java多线程的知识。