Java并发编程73道⾯试题及答案
1、在java中守护线程和本地线程区别?
java中的线程分为两种:守护线程(Daemon)和⽤户线程(User)。
任何线程都可以设置为守护线程和⽤户线程,通过⽅法Thread.setDaemon(bool on);true则把该线程设置为守护线程,反之则为⽤户线程。Thread.setDaemon()必须在Thread.start()之前调⽤,否则运⾏时会抛出异常。
两者的区别:
唯⼀的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线
程,JVM撤离。也可以理解为守护线程是JVM⾃动创建的线程(但不⼀定),⽤户线程是程序创建的线程;⽐如JVM的垃圾回收线程是⼀个守护线程,当所有线程已经撤离,不再产⽣垃圾,守护线程⾃然就没事可⼲了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会⾃动离开。
暴雪暂停大陆2、线程与进程的区别?
进程是操作系统分配资源的最⼩单元,线程是操作系统调度的最⼩单元。
⼀个程序⾄少有⼀个进程,⼀个进程⾄少有⼀个线程。
3、什么是多线程中的上下⽂切换?
originos系统最新版本多线程会共同使⽤⼀组计算机上的CPU,⽽线程数⼤于给程序分配的CPU数量时,为了让各个线程都有执⾏的机会,就需要轮转使⽤CPU。不同的线程切换使⽤CPU发⽣的切换数据等就是上下⽂切换。
4、死锁与活锁的区别,死锁与饥饿的区别?
死锁:是指两个或两个以上的进程(或线程)在执⾏过程中,因争夺资源⽽造成的⼀种互相等待的现象,若⽆外⼒作⽤,它们都将⽆法推进下去。
产⽣死锁的必要条件:
互斥条件:所谓互斥就是进程在某⼀时间内独占资源。
请求与保持条件:⼀个进程因请求资源⽽阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得资源,在末使⽤完之前,不能强⾏剥夺。
循环等待条件:若⼲进程之间形成⼀种头尾相接的循环等待资源关系。
活锁:任务或者执⾏者没有被阻塞,由于某些条件没有满⾜,导致⼀直重复尝试,失败,尝试,失败。
活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, ⽽处于死锁的实体表现为等待;活锁有可能⾃⾏解开,死锁则不能。
饥饿:⼀个或者多个线程因为种种原因⽆法获得所需要的资源,导致⼀直⽆法执⾏的状态。
Java中导致饥饿的原因:
⾼优先级线程吞噬所有的低优先级线程的CPU时间。
线程被永久堵塞在⼀个等待进⼊同步块的状态,因为其他线程总是能在它之前持续地对该同步块进⾏访问。
线程在等待⼀个本⾝也处于永久等待完成的对象(⽐如调⽤这个对象的wait⽅法),因为其他线程总是被持续地获得唤醒。
5、Java中⽤到的线程调度算法是什么?
采⽤时间⽚轮转的⽅式。可以设置线程的优先级,会映射到下层的系统上⾯的优先级上,如⾮特别需要,尽量不要⽤,防⽌线程饥饿。
6、什么是线程组,为什么在Java中不推荐使⽤?
ThreadGroup类,可以把线程归属到某⼀个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式。
为什么不推荐使⽤?因为使⽤有很多的安全隐患吧,没有具体追究,如果需要使⽤,推荐使⽤线程池。
7、为什么使⽤Executor框架?
1. 每次执⾏任务创建线程 new Thread()⽐较消耗性能,创建⼀个线程是⽐较耗时、耗资源的。
2. 调⽤ new Thread()创建的线程缺乏管理,被称为野线程,⽽且可以⽆限制的创建,线程之间的相互竞争会导致过多占⽤系统资源⽽导致系统
瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。
3. 接使⽤new Thread() 启动的线程不利于扩展,⽐如定时执⾏、定期执⾏、定时定期执⾏、线程中断等都不便实现。
8、在Java中Executor和Executors的区别?
Executors ⼯具类的不同⽅法按照我们的需求创建了不同的线程池,来满⾜业务的需求。
Executor 接⼝对象能执⾏我们的线程任务。
ExecutorService接⼝继承了Executor接⼝并进⾏了扩展,提供了更多的⽅法我们能获得任务执⾏的状态并且可以获取任务的返回值。
使⽤ThreadPoolExecutor 可以创建⾃定义线程池。
Future 表⽰异步计算的结果,他提供了检查计算是否完成的⽅法,以等待计算的完成,并可以使⽤get()⽅法获取计算的结果。
9、什么是原⼦操作?在Java Concurrency API中有哪些原⼦类(atomic classes)?
原⼦操作(atomic operation)意为”不可被中断的⼀个或⼀系列操作” 。
处理器使⽤基于对缓存加锁或总线加锁的⽅式来实现多处理器之间的原⼦操作。
在Java中可以通过锁和循环CAS的⽅式来实现原⼦操作。 CAS操作——Compare & Set,或是 Compare & Swap,现在⼏乎所有的CPU指令都⽀持CAS的原⼦操作。
原⼦操作是指⼀个不受其他操作影响的操作任务单元。原⼦操作是在多线程环境下避免数据不⼀致必须的⼿段。
java经典上机编程题int++并不是⼀个原⼦操作,所以当⼀个线程读取它的值并加1时,另外⼀个线程有可能会读到之前的值,这就会引发错误。
为了解决这个问题,必须保证增加操作是原⼦的,在JDK1.5之前我们可以使⽤同步技术来做到这⼀点。到
JDK1.5,urrent.atomic包提供了int和long类型的原⼦包装类,它们可以⾃动的保证对于他们的操作是原⼦的并且不需要使⽤同步。
urrent这个包⾥⾯提供了⼀组原⼦类。其基本的特性就是在多线程环境下,当有多个线程同时执⾏这些类的实例包含的⽅法时,具有排他性,即当某个线程进⼊⽅法,执⾏其中的指令时,不会被其他线程打断,⽽别的线程就像⾃旋锁⼀样,⼀直等到该⽅法执⾏完成,才由JVM从等待队列中
选择⼀个另⼀个线程进⼊,这只是⼀种逻辑上的理解。
10、Java Concurrency API中的Lock接⼝(Lock interface)是什么?对⽐同步它有什么优势?
Lock接⼝⽐同步⽅法和同步块提供了更具扩展性的锁操作。
他们允许更灵活的结构,可以具有完全不同的性质,并且可以⽀持多个相关类的条件对象。
它的优势有:
可以使锁更公平
可以使线程在等待锁的时候响应中断
apache服务器的服务目录可以让线程尝试获取锁,并在⽆法获取锁的时候⽴即返回或者等待⼀段时间
可以在不同的范围,以不同的顺序获取和释放锁
整体上来说Lock是synchronized的扩展版,Lock提供了⽆条件的、可轮询的(tryLock⽅法)、定时的(tryLock带参⽅法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition⽅法)锁操作。另外Lock的实现类基本都⽀持⾮公平锁(默认)和公平锁,synchronized只⽀持⾮公平锁,当然,在⼤部分情
况下,⾮公平锁是⾼效的选择。
11、什么是Executors框架?
Executor框架是⼀个根据⼀组执⾏策略调⽤,调度,执⾏和控制的异步任务的框架。
⽆限制的创建线程会引起应⽤程序内存溢出。所以创建⼀个线程池是个更好的的解决⽅案,因为可以限制线程的数量并且可以回收再利⽤这些线程。利⽤Executors框架可以⾮常⽅便的创建⼀个线程池。
12、什么是阻塞队列?阻塞队列的实现原理是什么?如何使⽤阻塞队列来实现⽣产者-消费者模型?
阻塞队列(BlockingQueue)是⼀个⽀持两个附加操作的队列。
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为⾮空。当队列满时,存储元素的线程会等待队列可⽤。
阻塞队列常⽤于⽣产者和消费者的场景,⽣产者是往队列⾥添加元素的线程,消费者是从队列⾥拿元素的线程。阻塞队列就是⽣产者存放元素的容器,⽽消费者也只从容器⾥拿元素。
JDK7提供了7个阻塞队列。分别是:
ArrayBlockingQueue :⼀个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :⼀个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :⼀个⽀持优先级排序的⽆界阻塞队列。
DelayQueue:⼀个使⽤优先级队列实现的⽆界阻塞队列。
SynchronousQueue:⼀个不存储元素的阻塞队列。
LinkedTransferQueue:⼀个由链表结构组成的⽆界阻塞队列。
LinkedBlockingDeque:⼀个由链表结构组成的双向阻塞队列。
13、什么是Callable和Future?
Callable接⼝类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且⽆法抛出返回结果的异常,⽽Callable功能更强⼤⼀些,被线程执⾏后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执⾏任务的返回值。
可以认为是带有回调的Runnable。
Future接⼝表⽰异步任务,是还没有完成的任务给出的未来结果。所以说Callable⽤于产⽣结果,Future⽤于获取结果。
delphixe开发app14、什么是FutureTask?
使⽤ExecutorService启动任务。
在Java并发程序中FutureTask表⽰⼀个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等⽅法。只有当运算完成的时候结果才能取回,如果运算尚未完成get⽅法将会阻塞。⼀个FutureTask对象可以对调⽤了Callable和Runnable的对象进⾏包装,由于FutureTask也是调⽤了Runnable接⼝所以它可以提交给Executor来执⾏。
Java 并发编程73道⾯试题及答案
运⾏状态(Running)
处于这个状态的线程占⽤CPU,执⾏程序代码。只有处于就绪状态的线程才有机会转到运⾏状态。
阻塞状态(Blocked)
阻塞状态是指线程因为某些原因放弃CPU,暂时停⽌运⾏。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进⼊就绪状态,它才有机会转到运⾏状态。
阻塞状态可分为以下3种:
① 位于对象等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运⾏状态时,如果执⾏了某个对象的wait()⽅法,Java虚
拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。
② 位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运⾏状态时,试图获得某个对象的同步锁时,如果该对象的
同步锁已经被其他线程占⽤,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。
③ 其他阻塞状态(Otherwise Blocked):当前线程执⾏了sleep()⽅法,或者调⽤了其他线程的join()⽅法,或者发出了I/O请求时,就会进
⼊这个状态。
死亡状态(Dead)
当线程退出run()⽅法时,就进⼊死亡状态,该线程结束⽣命周期。
19、为什么我们调⽤start()⽅法时会执⾏run()⽅法,为什么我们不能直接调⽤run()⽅法?
当你调⽤start()⽅法时你将创建新的线程,并且执⾏在run()⽅法⾥的代码。
但是如果你直接调⽤run()⽅法,它不会创建新的线程也不会执⾏调⽤线程的代码,只会把run⽅法当作普通⽅法去执⾏。
20、Java中你怎样唤醒⼀个阻塞的线程?
在Java发展史上曾经使⽤suspend()、resume()⽅法对于线程进⾏阻塞唤醒,但随之出现很多问题,⽐较典型的还是死锁问题。ospf使用哪三种不同的管理距离?
解决⽅案可以使⽤以对象为⽬标的阻塞,即利⽤Object类的wait()和notify()⽅法实现线程阻塞。
⾸先,wait、notify⽅法是针对对象的,调⽤任意对象的wait()⽅法都将导致线程阻塞,阻塞的同时也将释放该对象的锁,相应地,调⽤任意对象的notify()⽅法则将随机解除该对象阻塞的线程,但它需要重
新获取改对象的锁,直到获取成功才能往下执⾏;其次,wait、notify⽅法必须在synchronized块或⽅法中被调⽤,并且要保证同步块或⽅法的锁对象与调⽤wait、notify⽅法的对象是同⼀个,如此⼀来在调⽤wait之前当前线程就已经成功获取某对象的锁,执⾏wait阻塞后当前线程就将之前获取的对象锁释放。
21、在Java中CycliBarriar和
CountdownLatch有什么区别?
CyclicBarrier可以重复使⽤,⽽CountdownLatch不能重复使⽤。
Java的concurrent包⾥⾯的CountDownLatch其实可以把它看作⼀个计数器,只不过这个计数器的操作是原⼦操作,同时只能有⼀个线程去操作这个计数器,也就是同时只能有⼀个线程去减这个计数器⾥⾯的值。
你可以向CountDownLatch对象设置⼀个初始的数字作为计数值,任何调⽤这个对象上的await()⽅法都会阻塞,直到这个计数器的计数值被其他的线程减为0为⽌。
所以在当前计数到达零之前,await ⽅法会⼀直受阻塞。之后,会释放所有等待的线程,await的所有后续调⽤都将⽴即返回。这种现象只出现⼀次——计数⽆法被重置。如果需要重置计数,请考虑使⽤
CyclicBarrier。
CountDownLatch的⼀个⾮常典型的应⽤场景是:有⼀个任务想要往下执⾏,但必须要等到其他的任务执⾏完毕后才可以继续往下执⾏。假如我们这个想要继续往下执⾏的任务调⽤⼀个CountDownLatch对象的await()⽅法,其他的任务执⾏完⾃⼰的任务后调⽤同⼀个CountDownLatch 对象上的countDown()⽅法,这个调⽤await()⽅法的任务将⼀直阻塞等待,直到这个CountDownLatch对象的计数值减到0为⽌
CyclicBarrier⼀个同步辅助类,它允许⼀组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及⼀组固定⼤⼩的线程的

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。