java线程面试题及答案,java多线程编程面试题

  java线程面试题及答案,java多线程编程面试题

  本文总结了一些java多线程面试问答,有一定的参考价值。有需要的朋友可以参考一下,希望能帮到你。

  如何解决写爬虫IP受阻的问题?立即使用。

  什么是线程?

  线程是操作系统能够执行操作调度的最小单位。它包含在流程中,是流程中的实际操作单元。程序员可以使用它进行多处理器编程,您可以使用多线程来加速计算密集型任务。例如,如果一个线程完成一项任务需要100毫秒,那么十个线程完成任务更改只需要10毫秒。Java语言对多线程提供了极好的支持,也是一个很好的卖点。

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

  线程是进程的子集。一个进程可以有许多线程,每个线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享相同的内存空间。不要把它和堆栈内存混淆。每个线程都有自己的堆栈内存来存储本地数据。

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

  语言层面有两种方式。java.lang.Thread类的实例是一个线程,但是需要调用java.lang.Runnable接口来执行。由于线程类本身就是被调用的Runnable接口,所以可以继承java.lang.Thread类或者直接调用Runnable接口重写run()方法来实现线程。

  4) 用Runnable还是Thread?

  这个问题是上一个问题的后续。大家都知道我们可以通过继承Thread类或者调用Runnable接口来实现线程。问题是,哪种方法更好?什么情况下可以用?这个问题很好回答,如果你知道Java不支持类的多重继承,但是允许你调用多个接口。所以如果你想继承其他类,当然要调用Runnable接口。

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

  这个问题经常被问到,但还是能看出面试官对Java线程模型的理解。start()方法用于启动新创建的线程,start()内部调用run()方法,与直接调用run()方法的效果不同。调用run()方法时,只会在原线程中调用,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并发编程实践》的第16章,加深对Java内存模型的理解。

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

  Volatile是一个特殊的修饰符,只能由成员变量使用。当Java并发程序缺少同步类时,对成员变量的多线程操作对其他线程是透明的。volatile变量可以保证下一次读操作发生在前一次写操作之后,这就是上一题的volatile变量规则。

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

  如果您的代码位于多个线程同时运行的进程中,这些线程可能会同时运行该代码。如果每次运行的结果与单线程运行的结果相同,并且其他变量的值与预期相同,则是线程安全的。即使线程安全计数器类的同一个实例对象被多个线程使用,也不会出现计算错误。显然,您可以将集合类分为两组,线程安全的和非线程安全的。Vector通过同步方法是线程安全的,但是它类似的ArrayList不是线程安全的。

  12) Java中什么是竞态条件? 举个例子说明。

  竞争条件会导致并发程序中的一些错误。当多线程竞争某些资源时,就会出现争用情况。如果第一个要执行的程序竞争失败,后面执行,那么整个程序就会出现一些不确定的bug。这种bug很难发现,而且会因为线程之间的随机竞争而重复出现。

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

  Java提供了丰富的API,但是没有提供停止线程的API。JDK 1.0最初有一些控制方法,如stop()、suspend()和resume(),但由于潜在的死锁威胁,它们在随后的JDK版本中被放弃。在那之后,Java API设计者没有提供一个兼容的、线程安全的方法来停止线程。当run()或call()方法执行完毕时,线程将自动结束。如果想要手动结束线程,可以使用volatile布尔变量来退出run()方法的循环,或者取消任务来中断线程。

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

  这是我在一次面试中遇到的一个比较棘手的Java面试问题。简单来说,如果没有捕获到异常,线程将停止执行。线程。uncaughtException Handler是一个嵌入式接口,用于处理由未捕获的异常引起的线程突然中断。当uncaughtException将中断线程时,JVM将使用thread . getuncaughtexceptionhandler()查询线程的uncaughtException处理程序,并将线程和异常作为参数传递给处理程序的uncaughtException()方法进行处理。

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

  您可以通过共享对象或使用并发数据结构(如阻塞队列)来实现这一点。本教程《Java线程间通信》(涉及在两个线程之间共享对象)使用等待和通知方法实现了生产者-消费者模型。

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

  这是另一个棘手的问题,因为多线程可以等待单个监控锁。Java API的设计者提供了一些方法在等待条件改变时通知他们,但是这些方法并没有完全实现。notify()方法不能唤醒一个特定的线程,所以它只在一个线程等待时有用。而notifyAll()会唤醒所有线程,并允许它们竞争锁,以确保至少有一个线程可以继续运行。

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

  这是一个设计相关的问题,考察的是面试官对现有制度的看法,以及一些普遍存在但看似不合理的东西。在回答这些问题时,你要解释为什么把这些方法放在Object类中有意义,为什么不放在Thread类中。一个明显的原因是,JAVA提供的锁是对象级的,而不是线程级的。每个对象都有一个锁,它是通过线程获得的。如果线程需要等待一些锁,在对象中调用wait()方法是有意义的。如果wait()方法是在Thread类中定义的,那么线程正在等待哪个锁并不明显。简单来说,wait、notify和notifyAll都是锁级操作,所以在Object类中定义,因为锁属于对象。

  18) 什么是ThreadLocal变量?

  ThreadLocal是Java中的一个特殊变量。每个线程都有一个ThreadLocal,即每个线程都有自己的自变量,完全消除了竞态条件。对于创建昂贵的对象来说,这是获得线程安全的好方法。例如,可以使用ThreadLocal使SimpleDateFormat成为线程安全的。不值得在本地使用它,因为创建该类的成本很高,并且每个调用都需要创建不同的实例。如果每个线程都有自己唯一的变量副本,效率会大大提高。首先,通过多路复用减少了创建的昂贵对象的数量。其次,您无需使用代价高昂的同步或不变性就能获得线程安全。线程局部变量的另一个好例子是ThreadLocalRandom类,它减少了在多线程环境中创建的昂贵的随机对象的数量。

  19) 什么是FutureTask?

  在Java并发程序中,FutureTask代表一个可以取消的异步操作。它有启动和取消操作、查询操作是否完成以及检索操作结果等方法。只有当操作完成时,才能检索结果。如果操作没有完成,get方法将阻塞。FutureTask对象可以打包调用Callable和Runnable的对象。由于FutureTask也调用Runnable接口,所以可以提交给执行器执行。

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

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

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

  这主要是因为Java API要求这样做。否则,您的代码将抛出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参数用于控制线程的堆栈大小。

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

  在过去很长一段时间里,Java只能通过同步关键字来实现互斥,但它有一些缺点。例如,不能在锁外扩展方法或块边界,在试图获取锁时不能中途取消。Java通过锁接口提供了更复杂的控制来解决这些问题。ReentrantLock类实现了Lock,它具有与synchronized相同的并发性和内存语义,并且它也是可扩展的。

  33) 有三个线程T1,T2,T3,怎么确保它们按顺序执行?

  在多线程中,有许多方法可以让线程以特定的顺序执行。可以使用thread类的join()方法启动一个线程中的另一个线程,另一个线程会完成该线程,继续执行。为了确保三个线程的顺序,您应该首先启动最后一个线程(T3调用T2,T2调用T1),这样T1将首先完成,T3将最后完成。

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

  Yield方法可以暂停当前正在执行的线程对象,让具有相同优先级的其他线程执行它。它是一个静态方法,只保证当前线程放弃CPU占用,但不能保证其他线程可以占用CPU。执行yield()的线程可以在进入暂停状态后立即执行。

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

  ConcurrentHashMap将实际的Map分成几个部分,以实现其可伸缩性和线程安全性。这个分区是通过使用concurrency获得的,ConcurrentHashMap类的构造函数的一个可选参数。默认值是16,因此在多线程的情况下可以避免争用。

  36) Java中Semaphore是什么?

  Semaphore在Java中是一个新的同步类,是一个计数信号。从概念上讲,信号量维护一个权限集。如有必要,在许可变得可用之前,每个acquire()将被阻塞,然后许可将被获取。每个release()添加一个许可证,这可能会释放一个阻塞的收单方。然而,Semaphore并不使用实际的许可对象,而是只计算可用许可的数量,并采取相应的行动。信号量经常用在多线程代码中,比如数据库连接池。

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

  这个问题比较棘手,很多程序员认为这个任务会一直阻塞到线程池队列为空。事实上,如果一个任务不能被调度执行,那么ThreadPoolExecutor的submit()方法将抛出RejectedExecutionException异常。

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

  这两种方法都可以向线程池提交任务。execute()方法的返回类型是void,定义在Executor接口中,而submit()方法可以返回保存计算结果的Future对象,定义在ExecutorService接口中,扩展了Executor接口。其他线程池类如ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。

  39) 什么是阻塞式方法?

  阻塞方法意味着程序将等待该方法完成,不做任何其他事情。ServerSocket的accept()方法只是等待客户端连接。这里的阻塞是指在调用结果返回之前,当前线程会被挂起,直到得到结果才会返回。此外,还有在任务完成前返回的异步和非阻塞方法。

  40) Swing是线程安全的吗? 为什么?

  你绝对可以给出一个答案,Swing不是线程安全的,但是即使面试官没有问你为什么,你也要解释这个答案的原因。当我们说swing不是线程安全的时候,我们经常提到它的组件。这些组件不能在多线程中修改,对GUI组件的所有更新都必须在AWT线程中完成。Swing提供同步和异步回调方法来更新它们。

  41) Java中invokeAndWait 和 invokeLater有什么区别?

  Swing API为Java开发人员提供了这两种方法,以便从当前线程而不是事件调度线程更新GUI组件。InvokeAndWait()同步更新GUI组件,比如进度条。一旦进度被更新,进度条应该相应地改变。如果进度被多个线程跟踪,则调用invokeAndWait()方法来请求事件调度线程相应地更新组件。invokeLater()方法异步调用更新组件。

  42) Swing API中那些方法是线程安全的?

  这个问题中还提到了Swing和线程安全。虽然组件不是线程安全的,但是有一些方法可以被多线程安全地调用,比如repaint()、revalidate()。JTextComponent的setText()方法和JTextArea的insert()和append()方法也是线程安全的。

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

  这个问题看起来和多线程没什么关系,但是不变性有助于简化已经很复杂的并发程序。不可变对象可以在不同步的情况下共享,这减少了对该对象的并发访问的同步开销。然而,Java没有注释@Immutable。要创建一个不可变的类,需要实现以下步骤:通过构造方法初始化所有成员,不要为变量提供setter方法,将所有成员声明为私有,这样就不允许直接访问这些成员。在getter方法中,不是直接返回对象本身,而是克隆对象并返回对象的副本。我的文章《如何在Java中使对象不可变》有详细的教程。看完之后,你就可以自信了。

  44) Java中的ReadWriteLock是什么?

  一般来说,读写锁是用来提高并发程序性能的锁分离技术的结果。Java中的读写锁是Java 5中的一个新接口。一个读写锁维护一对关联锁,一个用于只读操作,一个用于写操作。在没有写线程的情况下,读锁可以由多个读线程同时持有。写锁是排他的,可以在JDK使用ReentrantReadWriteLock来实现这个规则。它最多支持65,535个写锁和65,535个读锁。

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

  繁忙循环是指程序员使用循环让线程等待。与wait()、sleep()或yield()等传统方法都放弃CPU控制权不同,一个繁忙的循环并没有放弃CPU,它只是在运行一个空循环。这样做的目的是保留CPU缓存。在多核系统中,当一个等待线程醒来时,它可能会在另一个内核中运行,这将重建缓存。它可用于避免重建缓存,并减少重建的等待时间。

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

  这是一个有趣的问题。首先,易变变量和原子变量看起来很像,但是作用不同。Volatile变量可以保证先行关系,即写操作会发生在后续读操作之前,但不能保证原子性。例如,如果用volatile修改count变量,那么count操作就不是原子的。AtomicInteger类提供的原子方法可以使这个操作原子化。例如,getAndIncrement()方法可以原子地执行增量操作以将当前值增加1,其他数据类型和引用变量也可以执行类似的操作。

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

  这个问题坑了很多Java程序员。如果你能想到锁是否释放了这条线索来回答它,你就有希望答对。不管你的sync块是正常退出还是异常退出,里面的线程都会释放锁,所以比起锁接口,我更喜欢sync块,因为它不需要我花什么力气去释放锁。这个功能可以通过释放finally块中的锁来实现。

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

  这个问题在Java面试中经常被问到,但是面试官回答这个问题的满意度只有50%。一半人不会写双重检查锁,另一半人说不出它的隐患和Java1.5是怎么修复的。实际上,这是一个用来创建线程安全单例的老方法。singleton实例刚创建的时候,试图用单锁来优化性能,但是在JDK1.4中失败了,因为太复杂了,我个人不喜欢。反正就算不喜欢也要了解一下,因为经常有人问。

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

  这是上一个问题的后续。如果你不喜欢双锁,面试官要求用另一种方法创建一个Singleton类,你可以使用JVM的类加载和静态变量初始化特性来创建一个Singleton实例,或者使用枚举类型来创建一个Singleton。我很喜欢这个方法。

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

  我最喜欢这种问题,我相信你在编写并发代码时会遵循一些最佳实践来提高性能。我认为大多数Java程序员应该遵循以下三个最佳实践:

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

  这使得查找或跟踪bug变得很容易。名称订单处理器、报价处理器和交易处理器比Thread-1好得多。线程2和线程3。给线程起一个与它要完成的任务相关的名字。所有主要的框架甚至JDK都遵循这个最佳实践。避免锁定和缩小同步范围。

  锁定是昂贵的,并且上下文切换需要更多的时间和空间。尽量减少同步和锁定的使用,以减少临界区。因此,与同步方法相比,我更喜欢同步块,因为它使我能够绝对控制锁。多用途同步类使用较少的等待和通知。

  首先,倒计时锁存、信号量、循环屏障、交换器等同步类简化了编码操作,而用等待和通知很难控制复杂的控制流。其次,这些类是由最好的公司编写和维护的,它们将在后续的JDK中继续优化和改进。使用这些更高级的同步工具,您的程序可以毫不费力地得到优化。多用并发集,少用同步集

  这是另一个易于遵循且有很大好处的最佳实践。并发集比同步集具有更好的可扩展性,所以在并发编程中最好使用并发集。如果下次需要使用map,应该首先想到使用ConcurrentHashMap。51) 如何强制启动一个线程?

  这个问题就像如何强制Java垃圾回收一样。现在还没有办法。虽然您可以使用System.gc()进行垃圾收集,但不能保证它一定会成功。Java中没有办法强制线程启动,它是由线程调度器控制的,Java不发布相关的API。

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

  ForJoin framework是JDK7中的一个高效工具,通过它Java开发者可以充分利用现代服务器上的多处理器。它是专门为那些可以递归划分成许多子模块的模块设计的,目的是利用所有可用的处理能力来提高程序的性能。ForJoin框架的一个很大的优点就是使用了工作窃取算法,能够完成更多任务的工作线程可以从其他线程窃取任务来执行。

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

  Java程序中的等待和睡眠都会引起某种停顿,可以满足不同的需求。wait()方法用于线程间通信。如果等待条件为真,其他线程醒来,就会释放锁,而sleep()方法只会释放CPU资源或者暂时停止当前线程的执行,而不会释放锁。

  更多关于java的面试知识,请访问Java面试问题板块!以上是值得收藏的java多线程面试问题(附答案)细节。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: