Java线程面试题

-《Java核心技术 卷一》html

- Java线程面试题 Top 50:http://www.importnew.com/12773.htmljava

- JAVA多线程和并发基础面试问答: http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-answers/程序员

- 15个顶级Java多线程面试题及回答:http://ifeve.com/15-java-faq/面试

- 其余博客算法

如有问题,欢迎提出,共同进步!数据库

1) 什么是线程?编程

线程是操做系统可以进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运做单位。程序员能够经过它进行多处理器编程,你可使用多线程对运算密集型任务提速。好比,若是一个线程完成一个任务要100毫秒,那么用十个线程完成该任务只需10毫秒。数组

2) 线程和进程有什么区别?缓存

一个进程是一个独立(self contained)的运行环境,它能够被看做一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程能够有不少线程,每条线程并行执行不一样的任务。不一样的进程使用不一样的内存空间,而全部的线程共享一片相同的内存空间。别把它和栈内存搞混,每一个线程都拥有单独的栈内存用来存储本地数据。安全

3) 如何在Java中实现线程?

有两种建立线程的方法:一是实现Runnable接口,而后将它传递给Thread的构造函数,建立一个Thread对象;二是直接继承Thread类。

4) 用Runnable仍是Thread?

这个问题是上题的后续,你们都知道咱们能够经过继承Thread类或者调用Runnable接口来实现线程,问题是,那个方法更好呢?什么状况下使用它?这个问题很容易回答,若是你知道Java不支持类的多重继承,但容许你调用多个接口。因此若是你要继承其余类,固然是调用Runnable接口好了。更多详细信息请点击这里。

6) Thread 类中的start() 和 run() 方法有什么区别?

start()方法被用来启动新建立的线程,使该被建立的线程状态变为可运行状态。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。若是咱们调用了Thread的run()方法,它的行为就会和普通的方法同样,直接运行run()方法。为了在新的线程中执行咱们的代码,必须使用Thread.start()方法。

7) Java中Runnable和Callable有什么不一样?

Runnable和Callable都表明那些要在不一样的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增长的。它们的主要区别是Callable的 call() 方法能够返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable能够返回装载有计算结果的Future对象。

8) Java中CyclicBarrier 和 CountDownLatch有什么不一样?

CyclicBarrier 和 CountDownLatch 均可以用来让一组线程等待其它线程。与 CyclicBarrier 不一样的是,CountdownLatch 不能从新使用。

9) Java内存模型是什么?

Java内存模型规定和指引Java程序在不一样的内存架构、CPU和操做系统间有肯定性地行为。它在多线程的状况下尤为重要。Java内存模型对一个线程所作的变更能被其它线程可见提供了保证,它们之间是先行发生关系。这个关系定义了一些规则让程序员在并发编程时思路更清晰。好比,先行发生关系确保了:

- 线程内的代码可以按前后顺序执行,这被称为程序次序规则。

- 对于同一个锁,一个解锁操做必定要发生在时间上后发生的另外一个锁定操做以前,也叫作管程锁定规则。

- 前一个对volatile的写操做在后一个volatile的读操做以前,也叫volatile变量规则。

- 一个线程内的任何操做必需在这个线程的start()调用以后,也叫做线程启动规则。

- 一个线程的全部操做都会在线程终止以前,线程终止规则。

- 一个对象的终结操做必需在这个对象构造完成以后,也叫对象终结规则。

- 可传递性

强烈建议你们阅读《Java并发编程实践》第十六章来加深对Java内存模型的理解。

10) Java中的volatile 变量是什么?

volatile是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺乏同步类的状况下,多线程对成员变量的操做对其它线程是透明的。volatile变量能够保证下一个读取操做会在前一个写操做以后发生。线程都会直接从内存中读取该变量而且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。

11) 什么是线程安全?Vector是一个线程安全类吗?

若是你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。若是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的状况下也不会出现计算失误。很显然你能够将集合类分红两组,线程安全和非线程安全的。Vector 是用同步方法来实现线程安全的, 而和它类似的ArrayList不是线程安全的。

12) Java中什么是竞态条件?

在大多数实际的多线程应用中,两个或两个以上的线程须要共享对同一数据的存取。若是i线程存取相同的对象,而且每个线程都调用了一个修改该对象状态的方法,将会发生什么呢?能够想象,线程彼此踩了对方的脚。根据线程访问数据的次序,可能会产生讹误的对象。这样的状况一般称为竞争条件。

13) Java中如何中止一个线程?

Java提供了很丰富的API但没有为中止线程提供API。JDK 1.0原本有一些像stop(), suspend() 和 resume()的控制方法,可是因为潜在的死锁威胁。所以在后续的JDK版本中他们被弃用了,以后Java API的设计者就没有提供一个兼容且线程安全的方法来中止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,若是要手动结束一个线程,能够用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。

14) 一个线程运行时发生异常会怎样?

若是异常没有被捕获该线程将会中止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常形成线程忽然中断状况的一个内嵌接口。当一个未捕获异常将形成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常做为参数传递给handler的uncaughtException()方法进行处理。

15) 如何在两个线程间共享数据?

你能够经过共享对象来实现这个目的,或者是使用像阻塞队列这样并发的数据结构。这篇教程《Java线程间通讯》(涉及到在两个线程间共享对象)用wait和notify方法实现了生产者消费者模型。

16) Java中notify 和 notifyAll有什么区别?

这又是一个刁钻的问题,由于多线程能够等待单监控锁,Java API 的设计人员提供了一些方法当等待条件改变的时候通知它们,可是这些方法没有彻底实现。notify()方法不能唤醒某个具体的线程,因此只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒全部线程并容许他们争夺锁确保了至少有一个线程能继续运行。

17) 为何wait, notify 和 notifyAll这些方法不在thread类里面?

一个很明显的缘由是JAVA提供的锁是对象级的而不是线程级的,每一个对象都有锁,经过线程得到。若是线程须要等待某些锁那么调用对象中的wait()方法就有意义了。若是wait()方法定义在Thread类中,线程正在等待的是哪一个锁就不明显了。简单的说,因为wait,notify和notifyAll都是锁级别的操做,因此把他们定义在Object类中由于锁属于对象。

18) 什么是ThreadLocal变量?

ThreadLocal是Java里一种特殊的变量。每一个线程都有一个ThreadLocal就是每一个线程都拥有了本身独立的一个变量,竞争条件被完全消除了。若是为每一个线程提供一个本身独有的变量拷贝,将大大提升效率。首先,经过复用减小了代价高昂的对象的建立个数。其次,你在没有使用高代价的同步或者不变性的状况下得到了线程安全。

19) 什么是FutureTask?

在Java并发程序中FutureTask表示一个能够取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,若是运算还没有完成get方法将会阻塞。一个FutureTask对象能够对调用了Callable和Runnable的对象进行包装,因为FutureTask也是调用了Runnable接口因此它能够提交给Executor来执行。

20) Java中interrupted 和 isInterruptedd方法的区别?

interrupted() 和 isInterrupted()的主要区别是前者会将中断状态清除然后者不会。Java多线程的中断机制是用内部标识来实现的,调用Thread.interrupt()来中断一个线程就会设置中断标识为true。当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被清零。而非静态方法isInterrupted()用来查询其它线程的中断状态且不会改变中断状态标识。简单的说就是任何抛出InterruptedException异常的方法都会将中断状态清零。不管如何,一个线程的中断状态有有可能被其它线程调用中断来改变。

21) 为何wait和notify方法要在同步块中调用?

当一个线程须要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其余线程调用这个对象上的notify()方法。一样的,当一个线程须要调用对象的notify()方法时,它会释放这个对象的锁,以便其余在等待的线程就能够获得这个对象锁。因为全部的这些方法都须要线程持有对象的锁,这样就只能经过同步来实现,因此他们只能在同步方法或者同步块中被调用。若是你不这么作,代码会抛出IllegalMonitorStateException异常。

22) 为何你应该在循环中检查等待条件?

处于等待状态的线程可能会收到错误警报和伪唤醒,若是不在循环中检查等待条件,程序就会在没有知足结束条件的状况下退出。所以,当一个等待线程醒来时,不能认为它原来的等待状态仍然是有效的,在notify()方法调用以后和等待线程醒来以前这段时间它可能会改变。这就是在循环中使用wait()方法效果更好的缘由,你能够在Eclipse中建立模板调用wait和notify试一试。若是你想了解更多关于这个问题的内容,推荐你阅读《Effective Java》这本书中的线程和同步章节。

23) Java中的同步集合与并发集合有什么区别?

同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5以前程序员们只有同步集合来用且在多线程并发的时候会致使争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不只提供线程安全还用锁分离和内部分区等现代技术提升了可扩展性。更多内容详见答案。

24) Java中堆和栈有什么不一样?

为何把这个问题归类在多线程和并发面试题里?由于栈是一块和线程紧密相关的内存区域。每一个线程都有本身的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是全部线程共享的一片公用内存区域。对象都在堆里建立,为了提高效率线程会从堆中弄一个缓存到本身的栈,若是多个线程使用该变量就可能引起问题,这时volatile 变量就能够发挥做用了,它要求线程从主存中读取变量的值。

更多内容详见答案。

25) 什么是线程池? 为何要使用它?

建立线程要花费昂贵的资源和时间,若是任务来了才建立线程那么响应时间会变长,并且一个进程能建立的线程数有限。为了不这些问题,在程序启动的时候就建立若干线程来响应处理,它们被称为线程池,里面的线程叫工做线程。从JDK1.5开始,Java API提供了Executor框架让你能够建立不一样的线程池。好比单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合不少生存期短的任务的程序的可扩展线程池)。

26) 如何写代码来解决生产者消费者问题?

在现实中你解决的许多线程问题都属于生产者消费者模型,就是一个线程生产任务供其它线程进行消费,你必须知道怎么进行线程间通讯来解决这个问题。比较低级的办法是用wait和notify来解决这个问题,比较赞的办法是用Semaphore 或者 BlockingQueue来实现生产者消费者模型。

27) 如何避免死锁?

Java多线程中的死锁

死锁是指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。这是一个严重的问题,由于死锁会让你的程序挂起没法完成任务,死锁的发生必须知足如下四个条件:

- 互斥条件:一个资源每次只能被一个进程使用。

- 请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。

- 不剥夺条件:进程已得到的资源,在末使用完以前,不能强行剥夺。

- 循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。

避免死锁最简单的方法就是阻止循环等待条件,将系统中全部的资源设置标志位、排序,规定全部的进程申请资源必须以必定的顺序(升序或降序)作操做来避免死锁。

28) Java中活锁和死锁有什么区别?

这是上题的扩展,活锁和死锁相似,不一样之处在于处于活锁的线程或进程的状态是不断改变的,活锁能够认为是一种特殊的饥饿。一个现实的活锁例子是两我的在狭小的走廊碰到,两我的都试着避让对方好让彼此经过,可是由于避让的方向都同样致使最后谁都不能经过走廊。简单的说就是,活锁和死锁的主要区别是前者进程的状态能够改变可是却不能继续执行。

29) 怎么检测一个线程是否拥有锁?

在java.lang.Thread中有一个方法叫holdsLock(),它返回true若是当且仅当当前线程拥有某个具体对象的锁。

30) 你如何在Java中获取线程堆栈?

对于不一样的操做系统,有多种方法来得到Java进程的线程堆栈。当你获取线程堆栈时,JVM会把全部线程的状态存到日志文件或者输出到控制台。在Windows你可使用Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令。你也能够用jstack这个工具来获取,它对线程id进行操做,你能够用jps这个工具找到id。

31) JVM中哪一个参数是用来控制线程的栈堆栈小的

这个问题很简单, -Xss参数用来控制线程的堆栈大小。你能够查看JVM配置列表来了解这个参数的更多信息。

32) Java中synchronized 和 ReentrantLock 有什么不一样?

Java在过去很长一段时间只能经过synchronized关键字来实现互斥,它有一些缺点。好比你不能扩展锁以外的方法或者块边界,尝试获取锁时不能中途取消等。Java 5 经过Lock接口提供了更复杂的控制来解决这些问题。 ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义且它还具备可扩展性。

33) 有三个线程T1,T2,T3,怎么确保它们按顺序执行(确保main()方法所在的线程是Java程序最后结束的线程)?

在多线程中有多种方法让线程按特定顺序执行,你能够用线程类的join()方法在一个线程中启动另外一个线程,另一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。

34) Thread类中的yield方法有什么做用?

yield方法能够暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法并且只保证当前线程放弃CPU占用而不能保证使其它线程必定能占用CPU,执行yield()的线程有可能在进入到暂停状态后立刻又被执行。点击这里查看更多yield方法的相关内容。

35) Java中ConcurrentHashMap的并发度是什么?

ConcurrentHashMap把实际map划分红若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度得到的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程状况下就能避免争用。

36) Java中Semaphore是什么?

Java中的Semaphore是一种新的同步类,它是一个计数信号。从概念上讲,从概念上讲,信号量维护了一个许可集合。若有必要,在许可可用前会阻塞每个 acquire(),而后再获取该许可。每一个 release()添加一个许可,从而可能释放一个正在阻塞的获取者。可是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采起相应的行动。信号量经常用于多线程的代码中,好比数据库链接池。更多详细信息请点击这里。

37)若是你提交任务时,线程池队列已满。会时发会生什么?

这个问题问得很狡猾,许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上若是一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。

38) Java线程池中submit() 和 execute()方法有什么区别?

两个方法均可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中, 而submit()方法能够返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。更多详细信息请点击这里。

39) 什么是阻塞式方法?

阻塞式方法是指程序会一直等待该方法完成期间不作其余事情,ServerSocket的accept()方法就是一直等待客户端链接。这里的阻塞是指调用结果返回以前,当前线程会被挂起,直到获得结果以后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。更多详细信息请点击这里。

40) 你对线程优先级的理解是什么?

每个线程都是有优先级的,通常来讲,高优先级的线程在运行时会具备优先权,但这依赖于线程调度的实现,这个实现是和操做系统相关的(OS dependent)。咱们能够定义线程的优先级,可是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1表明最低优先级,10表明最高优先级。

41) 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

线程调度器是一个操做系统服务,它负责为Runnable状态的线程分配CPU时间。一旦咱们建立一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间能够基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,因此由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

42) 在多线程中,什么是上下文切换(context-switching)?

上下文切换是存储和恢复CPU状态的过程,它使得线程执行可以从中断点恢复执行。上下文切换是多任务操做系统和多线程环境的基本特征。

43) 如何在Java中建立Immutable对象?

Immutable对象能够在没有同步的状况下共享,下降了对该对象进行并发访问时的同步化开销。要建立不可变类,要实现下面几个步骤:经过构造方法初始化全部成员、对变量不要提供setter方法、将全部的成员声明为私有的,这样就不容许直接访问这些成员、在getter方法中,不要直接返回对象自己,而是克隆对象,并返回对象的拷贝。

44) Java中的ReadWriteLock是什么?

通常而言,读写锁是用来提高并发程序性能的锁分离技术的成果。Java中的ReadWriteLock是Java 5 中新增的一个接口,一个ReadWriteLock维护一对关联的锁,一个用于只读操做一个用于写。在没有写线程的状况下一个读锁可能会同时被多个读线程持有。写锁是独占的,你可使用JDK中的ReentrantReadWriteLock来实现这个规则,它最多支持65535个写锁和65535个读锁。

45) 多线程中的忙循环是什么?

忙循环就是程序员用循环让一个线程等待,不像传统方法wait(), sleep() 或 yield() 它们都放弃了CPU控制,而忙循环不会放弃CPU,它就是在运行一个空循环。这么作的目的是为了保留CPU缓存,在多核系统中,一个等待线程醒来的时候可能会在另外一个内核运行,这样会重建缓存。为了不重建缓存和减小等待重建的时间就可使用它了。

46)volatile 变量和 atomic 变量有什么不一样?

这是个有趣的问题。首先,volatile 变量和 atomic 变量看起来很像,但功能却不同。Volatile变量能够确保先行关系,即写操做会发生在后续的读操做以前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操做就不是原子性的。而AtomicInteger类提供的atomic方法可让这种操做具备原子性如getAndIncrement()方法会原子性的进行增量操做把当前值加一,其它数据类型和引用变量也能够进行类似操做。

47) 若是同步块内的线程抛出异常会发生什么?

这个问题坑了不少Java程序员,若你能想到锁是否释放这条线索来回答还有点但愿答对。不管你的同步块是正常仍是异常退出的,里面的线程都会释放锁,因此对比锁接口咱们更喜欢同步块,由于它不用花费精力去释放锁,该功能能够在finally block里释放锁实现。

48) 单例模式的双检锁是什么?

这个问题在Java面试中常常被问到,可是面试官对回答此问题的满意度仅为50%。一半的人写不出双检锁还有一半的人说不出它的隐患和Java1.5是如何对它修正的。它实际上是一个用来建立线程安全的单例的老方法,当单例实例第一次被建立时它试图用单个锁进行性能优化,可是因为太过于复杂在JDK1.4中它是失败的。

49) 如何在Java中建立线程安全的Singleton?

这是上面那个问题的后续,若是你不喜欢双检锁而面试官问了建立Singleton类的替代方法,你能够利用JVM的类加载和静态变量初始化特征来建立Singleton实例,或者是利用枚举类型来建立Singleton。

50) 写出3条你遵循的多线程最佳实践

如下三条最佳实践大多数Java程序员都应该遵循:

- 给你的线程起个有意义的名字。

这样能够方便找bug或追踪。OrderProcessor, QuoteProcessor or TradeProcessor 这种名字比 Thread-1. Thread-2 and Thread-3 好多了,给线程起一个和它要完成的任务相关的名字,全部的主要框架甚至JDK都遵循这个最佳实践。

- 避免锁定和缩小同步的范围

锁花费的代价高昂且上下文切换更耗费时间空间,试试最低限度的使用同步和锁,缩小临界区。所以相对于同步方法我更喜欢同步块,它给我拥有对锁的绝对控制权。

- 多用同步类少用wait 和 notify

首先,CountDownLatch, Semaphore, CyclicBarrier 和 Exchanger 这些同步类简化了编码操做,而用wait和notify很难实现对复杂控制流的控制。其次,这些类是由最好的企业编写和维护在后续的JDK中它们还会不断优化和完善,使用这些更高等级的同步工具你的程序能够不费吹灰之力得到优化。

- 多用并发集合少用同步集合

这是另一个容易遵循且受益巨大的最佳实践,并发集合比同步集合的可扩展性更好,因此在并发编程时使用并发集合效果更好。若是下一次你须要用到map,你应该首先想到用ConcurrentHashMap。

51) 如何强制启动一个线程?

这个问题就像是如何强制进行Java垃圾回收,目前尚未以为方法,虽然你可使用System.gc()来进行垃圾回收,可是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。

52) Java中的fork join框架是什么?

fork join框架是JDK7中出现的一款高效的工具,Java开发人员能够经过它充分利用现代服务器上的多处理器。它是专门为了那些能够递归划分红许多子模块设计的,目的是将全部可用的处理能力用来提高程序的性能。fork join框架一个巨大的优点是它使用了工做窃取算法,能够完成更多任务的工做线程能够从其它线程中窃取任务来执行。

53) Java多线程中调用wait() 和 sleep()方法有什么不一样?

Java程序中wait 和 sleep都会形成某种形式的暂停,它们能够知足不一样的须要。wait()方法用于线程间通讯,若是等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程中止执行一段时间,但不会释放锁。须要注意的是,sleep()并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,而且根据线程调度,它将获得执行。

54) 什么是Thread Group?为何不建议使用它?

ThreadGroup是一个类,它的目的是提供关于线程组的信息。

ThreadGroup API比较薄弱,它并无比Thread提供了更多的功能。它有两个主要的功能:一是获取线程组中处于活跃状态线程的列表;二是设置为线程设置未捕获异常处理器(ncaught exception handler)。但在Java 1.5中Thread类也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,因此ThreadGroup是已通过时的,不建议继续使用。

55) 什么是Java线程转储(Thread Dump),如何获得它?

线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁很是有用。有不少方法能够获取线程转储——使用Profiler,Kill -3命令,jstack工具等等。咱们更喜欢jstack工具,由于它容易使用而且是JDK自带的。因为它是一个基于终端的工具,因此咱们能够编写一些脚本去定时的产生线程转储以待分析。

56) 什么是Java Timer类?如何建立一个有特定时间间隔的任务?

java.util.Timer是一个工具类,能够用于安排一个线程在将来的某个特定时间执行。Timer类能够用安排一次性任务或者周期任务。

java.util.TimerTask是一个实现了Runnable接口的抽象类,咱们须要去继承这个类来建立咱们本身的定时任务并使用Timer去安排它的执行。

57) 什么是原子操做?在Java Concurrency API中有哪些原子类(atomic classes)?

原子操做是指一个不受其余操做影响的操做任务单元。原子操做是在多线程环境下避免数据不一致必须的手段。

int++并非一个原子操做,因此当一个线程读取它的值并加1时,另一个线程有可能会读到以前的值,这就会引起错误。

在 java.util.concurrent.atomic 包中添加原子变量类以后,这种状况才发生了改变。全部原子变量类都公开比较并设置原语(与比较并交换相似),这些原语都是使用平台上可用的最快本机结构(比较并交换、加载连接/条件存储,最坏的状况下是旋转锁)来实现的。 java.util.concurrent.atomic 包中提供了原子变量的 9 种风格( AtomicInteger; AtomicLong; AtomicReference; AtomicBoolean;原子整型;长型;引用;及原子标记引用和戳记引用类的数组形式,其原子地更新一对值)。

58) Java Concurrency API中的Lock接口(Lock interface)是什么?对比同步它有什么优点?

Lock接口比同步方法和同步块提供了更具扩展性的锁操做。他们容许更灵活的结构,能够具备彻底不一样的性质,而且能够支持多个相关类的条件对象。

它的优点有:

- 可使锁更公平

- 可使线程在等待锁的时候响应中断

- 可让线程尝试获取锁,并在没法获取锁的时候当即返回或者等待一段时间

- 能够在不一样的范围,以不一样的顺序获取和释放锁

59) 什么是Executor框架?

Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入。Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。

无限制的建立线程会引发应用程序内存溢出。因此建立一个线程池是个更好的的解决方案,由于能够限制线程的数量而且能够回收再利用这些线程。利用Executor框架能够很是方便的建立一个线程池。

60) Executors类是什么?

Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法。

Executors能够用于方便的建立线程池。

61) 什么是阻塞队列?如何使用阻塞队列来实现生产者-消费者模型?

java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操做将会被阻塞,或者当队列是满时,往队列里添加元素的操做会被阻塞。

阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException。

阻塞队列的实现都是线程安全的,全部的查询方法都是原子的而且使用了内部锁或者其余形式的并发控制。

BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。

62)什么是Callable和Future?

Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很类似,但它能够返回一个对象或者抛出一个异常。

Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。因为Callable任务是并行的,咱们必须等待它返回的结果。java.util.concurrent.Future对象为咱们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它咱们能够知道Callable任务的状态和获得Callable返回的执行结果。Future提供了get()方法让咱们能够等待Callable结束并获取它的执行结果。

63) 什么是FutureTask?

FutureTask包装器是一种很是便利的机制,可将Callable转换成Future和Runnable,它同时实现二者的接口。

FutureTask类是Future 的一个实现,并实现了Runnable,因此可经过Excutor(线程池) 来执行。也可传递给Thread对象执行。若是在主线程中须要执行比较耗时的操做时,但又不想阻塞主线程时,能够把这些做业交给Future对象在后台完成,当主线程未来须要时,就能够经过Future对象得到后台做业的计算结果或者执行状态。

64) 什么是并发容器的实现?

Java集合类都是快速失败的,这就意味着当集合被改变且一个线程在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。

并发容器:并发容器是针对多个线程并发访问设计的,在jdk5.0引入了concurrent包,其中提供了不少并发容器,如ConcurrentHashMap,CopyOnWriteArrayList等。并发容器使用了与同步容器彻底不一样的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种粒度更细的加锁机制,能够称为分段锁,在这种锁机制下,容许任意数量的读线程并发地访问map,而且执行读操做的线程和写操做的线程也能够并发的访问map,同时容许必定数量的写操做线程并发地修改map,因此它能够在并发环境下实现更高的吞吐量。

65)用户线程和守护线程有什么区别?

当咱们在Java程序中建立一个线程,它就被称为用户线程。一个守护线程是在后台执行而且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序而且退出。一个守护线程建立的子线程依然是守护线程。

66)有哪些不一样的线程生命周期?

当咱们在Java程序中新建一个线程时,它的状态是New。当咱们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间而且讲它们的状态改变为Running。其余的线程状态还有Waiting,Blocked 和Dead。

67)线程之间是如何通讯的?

当线程间是能够共享资源时,线程间通讯是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法能够用于线程间通讯关于资源的锁的状态。

68)为何Thread类的sleep()和yield()方法是静态的?

Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。因此在其余处于等待状态的线程上调用这些方法是没有意义的。这就是为何这些方法是静态的。它们能够在当前正在执行的线程中工做,并避免程序员错误的认为能够在其余非运行线程调用这些方法。

69)如何确保线程安全?

在Java中能够有不少方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。

70)同步方法和同步块,哪一个是更好的选择?

同步块是更好的选择,由于它不会锁住整个对象(固然你也可让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这一般会致使他们中止执行并须要等待得到这个对象上的锁。

71)如何建立守护线程?

使用Thread类的setDaemon(true)方法能够将线程设置为守护线程,须要注意的是,须要在调用start()方法前调用这个方法,不然会抛出IllegalThreadStateException异常。

72)线程调度策略?

(1) 抢占式调度策略

Java运行时系统的线程调度算法是抢占式的 (preemptive)。Java运行时系统支持一种简单的固定优先级的调度算法。若是一个优先级比其余任何处于可运行状态的线程都高的线程进入就绪状态,那么运行时系统就会选择该线程运行。新的优先级较高的线程抢占(preempt)了其余线程。可是Java运行时系统并不抢占同优先级的线程。换句话说,Java运行时系统不是分时的(time-slice)。然而,基于Java Thread类的实现系统多是支持分时的,所以编写代码时不要依赖分时。当系统中的处于就绪状态的线程都具备相同优先级时,线程调度程序采用一种简单的、非抢占式的轮转的调度顺序。

(2) 时间片轮转调度策略

有些系统的线程调度采用时间片轮转(round-robin)调度策略。这种调度策略是从全部处于就绪状态的线程中选择优先级最高的线程分配必定的CPU时间运行。该时间事后再选择其余线程运行。只有当线程运行结束、放弃(yield)CPU或因为某种缘由进入阻塞状态,低优先级的线程才有机会执行。若是有两个优先级相同的线程都在等待CPU,则调度程序以轮转的方式选择运行的线程。

73) 在线程中你怎么处理不可捕捉异常?

Thread.UncaughtExceptionHandler是java SE5中的新接口,它容许咱们在每个Thread对象上添加一个异常处理器。

相关文章
相关标签/搜索