b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

Java多线程面试问题

电脑杂谈  发布时间:2020-07-09 10:13:46  来源:网络整理

避免线程死锁_java多线程避免死锁_java线程死锁怎么解决

线程是操作系统可以执行操作调度的最小单元. 它包含在过程中,并且是过程中的实际操作单元. 程序员可以将其用于多处理器编程,并且您可以使用多线程来加速计算密集型任务. 例如,如果一个线程花费100毫秒来完成一个任务,那么仅用10毫秒就可以完成十个线程的任务. Java在语言级别为多线程提供了出色的支持,这也是一个很好的卖点.

线程是进程的子集. 一个进程可以有很多线程,并且每个线程并行执行不同的任务. 不同的进程使用不同的内存空间,并且所有线程共享相同的内存空间. 不要将其与堆栈内存混淆,每个线程都有一个单独的堆栈内存来存储本地数据.

在语言级别有两种方法. java.lang.Thread类的实例是一个线程,但是它需要调用java.lang.Runnable接口来执行. 由于线程类本身就是所谓的Runnable接口,因此您可以继承java.lang.Thread类,也可以直接调用Runnable接口以重复编写run()方法以实现线程.

此问题是上一个问题的后续. 每个人都知道我们可以通过继承Thread类或调用Runnable接口来实现线程. 问题是,哪种方法更好?什么时候使用?如果您知道Java不支持类的多重继承,但允许您调用多个接口,则此问题很容易回答. 因此,如果您想继承其他类,请调用Runnable接口.

这个问题经常被问到,但是仍然有可能区分访问者对Java线程模型的理解程度. start()方法用于启动新创建的线程,run()方法在start()中内部调用,这与直接调用run()方法不同. 当您调用run()方法时,它将仅在原始线程中被调用. 如果没有要启动的新线程,则start()方法将启动新线程.

Runnable和Callable表示要在不同线程中执行的任务. 从JDK1.0开始就可以使用Runnable,在JDK1.5中添加了Callable. 主要区别在于Callable的call()方法可以返回值并引发异常,而Runnable的run()方法没有这些功能. Callable可以返回加载了计算结果的Future对象.

CyclicBarrier和CountDownLatch可用于使一组线程等待其他线程. 与CyclicBarrier不同,CountdownLatch无法重复使用.

Java内存模型指定并指导Java程序在不同的内存体系结构,CPU和操作系统之间确定性地运行. 在多线程的情况下,这一点尤其重要. Java内存模型中一个线程所做的更改可以被其他线程看到,以保证它们之间的关系首先发生. 这种关系定义了一些规则,以使程序员在并发编程中思路更加清晰. 例如,首先建立关系可以确保:

我强烈建议您阅读“ Java并行编程实践”的第16章,以加深对Java内存模型的理解.

volatile是一个特殊的修饰符,只有成员变量可以使用它. 在Java并发程序中没有同步类的情况下,多个线程对成员变量的操作对其他线程是透明的. volatile变量可以确保下一个读操作将在上一个写操作之后发生,这是上一个问题的volatile变量规则.

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

避免线程死锁_java多线程避免死锁_java线程死锁怎么解决

竞态条件在并发情况下会导致程序中的一些错误. 当多个线程争用某些资源时,将生成竞争条件. 如果第一个要执行的程序失败了,而比赛随后又执行了,那么整个程序将有一些不确定的错误. 此类错误很难找到,并且由于线程之间的随机竞争而会再次出现.

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

这是我在采访中遇到的一个非常棘手的Java采访问题. 简而言之,如果未捕获到异常,则线程将停止执行. Thread.UncaughtExceptionHandler是一个嵌入式接口,用于处理由未捕获的异常导致的线程突然中断. 当未捕获的异常导致线程中断时,JVM将使用Thread.getUncaughtExceptionHandler()查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给处理程序的uncaughtException()方法进行处理.

您可以通过共享对象或使用并发数据结构(例如阻塞队列)来实现此目的. 本教程“ Java线程间通信”(涉及两个线程之间的对象共享)使用等待和通知方法实现生产者使用者模型.

这是另一个棘手的问题,因为多线程可以等待单个监视器锁定,因此Java API的设计人员提供了一些在条件改变时通知它们的方法,但是这些方法并未完全实现. notify()方法无法唤醒特定线程,因此在等待时只有一个线程有用. NotifyAll()唤醒所有线程,并允许它们竞争锁,以确保至少一个线程可以继续运行.

这是与设计有关的问题. 它检查了访调员对现有系统和普遍存在但似乎不合理的事物的看法. 在回答这些问题时,您必须解释为什么将这些方法放在Object类中是有意义的,为什么不将它们放在Thread类中. 一个明显的原因是JAVA提供的锁是在对象级别而不是线程级别. 每个对象都有一个锁,该锁是通过线程获得的. 如果线程需要等待一些锁定,则可以在对象中调用wait()方法. 如果在Thread类中定义了wait()方法,则线程正在等待哪个锁并不明显. 简而言之,因为wait,notify和notifyAll是锁级别的操作,所以它们在Object类中定义,因为锁属于该对象.

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

并发Java程序中的

FutureTask表示可以取消的异步操作. 它具有启动和取消操作,查询操作是否完成以及检索操作结果的方法. 仅当操作完成后才能检索结果,如果操作尚未完成,则get方法将阻塞. FutureTask对象可以包装调用Callable和Runnable的对象. 由于FutureTask也调用Runnable接口,因此可以将其提交给Executor执行.

interrupted()和isInterrupted()之间的主要区别在于,前者将清除中断状态,而后者将不清除中断状态. Java多线程的中断机制是通过内部标识符实现的. 调用Thread.interrupt()中断线程会将中断标识符设置为true. 当被中断的线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态将被清除. 非静态方法isInterrupted()用于查询其他线程的中断状态,而不更改中断状态标志. 简而言之,任何引发InterruptedException的方法都将清除中断状态. 在任何情况下,线程的中断状态都可以由其他线程调用中断来更改.

主要是因为Java API强制执行此操作. 如果不这样做,您的代码将抛出IllegalMonitorStateException. 另一个原因是要避免在等待和通知之间出现竞争状况.

java多线程避免死锁_java线程死锁怎么解决_避免线程死锁

处于等待状态的线程可能会收到错误的警报和错误的唤醒. 如果循环中未检查等待条件,则程序将不满足结束条件而退出. 因此,当等待线程唤醒时,不能认为其原始等待状态仍然有效. 在调用notify()方法之后以及在等待线程唤醒之前,它可能会更改. 这就是为什么在循环中使用wait()方法效果更好的原因. 您可以在Eclipse中创建一个模板,然后调用wait并通知进行尝试. 如果您想了解有关此问题的更多信息,建议您阅读“有效Java”一书中的“线程和同步”一章.

同步集合和并发集合为多线程和并发提供了合适的线程安全集合,但是并发集合具有更高的可伸缩性. 在Java 1.5之前,程序员仅使用同步集,并在多线程并发时引起争用,这阻碍了系统的可伸缩性. Java 5引入了诸如ConcurrentHashMap之类的并发集合,该集合不仅提供线程安全性,而且还使用诸如锁分离和内部分区之类的现代技术来提高可伸缩性.

为什么将此问题归类为多线程和并发面试问题?因为堆栈是与线程紧密相关的内存区域. 每个线程都有自己的堆栈内存,用于存储局部变量,方法参数和堆栈调用. 一个线程中存储的变量对其他线程不可见. 堆是所有线程共享的公共内存区域. 在堆中创建对象. 为了提高效率,线程将从堆中获取一个缓存到其自己的堆栈. 如果多个线程使用此变量,则可能会导致问题. 此时,volatile变量可以起作用. 它要求线程从主线程开始. 读取内存中变量的值.

创建线程需要花费大量资源和时间. 如果任务在创建线程之前完成,则响应时间将变长,并且进程可以创建的线程数受到限制. 为避免这些问题,在启动程序时会创建多个线程来响应处理. 它们称为线程池,其中的线程称为工作线程. 从JDK1.5开始,Java API提供了Executor框架,以便您可以创建不同的线程池. 例如,单线程池一次处理一个任务. 固定数量的线程池或缓存的线程池(可扩展的线程池,适用于具有许多短期任务的程序).

实际上,您解决的许多线程问题都属于生产者-消费者模型,也就是说,线程生产任务被其他线程消耗. 您必须知道如何程之间进行通信才能解决此问题. 下层方法是使用等待和通知来解决此问题. 更优选的方法是使用Semaphore或BlockingQueue来实现生产者-消费者模型. 本教程将实现它.

Java多线程中的死锁

死锁是指在执行两个或多个进程期间由于资源竞争而互相等待的现象. 没有外力,他们将无法前进. 这是一个严重的问题,因为死锁将导致您的程序挂起并且无法完成任务. 死锁的发生必须满足以下四个条件:

避免死锁的最简单方法是防止循环等待条件,在系统标志位中设置所有资源,进行排序,并规定所有申请资源的进程必须以一定顺序(升序或降序)运行,以避免死锁.

这是上述问题的扩展. 活锁类似于死锁. 区别在于,活动锁中的线程或进程的状态不断变化. 活锁可以被视为一种特殊的饥饿感. 一个活锁的现实例子是两个人在狭窄的走廊里相遇. 他们都试图互相避开,以便彼此通过,但是由于避开的方向相同,所以没有人可以通过走廊. 简而言之,活动锁和死锁之间的主要区别在于,前一个进程的状态可以更改,但不能继续执行.

在参加电话采访之前,我什至不知道我们甚至无法检测到线程是否拥有锁. 在且仅当当前线程拥有对特定对象的锁时,java.lang.Thread中有一种称为holdLock()的方法返回true.

避免线程死锁_java线程死锁怎么解决_java多线程避免死锁

对于不同的操作系统,有多种方法来获取Java进程的线程堆栈. 当您获得线程堆栈时,JVM会将所有线程的状态保存到日志文件或输出到控制台. 在Windows上,可以使用Ctrl + Break键组合获取线程堆栈. 在Linux上,使用kill -3命令. 您也可以使用工具jstack来获取它. 它对线程ID进行操作. 您可以使用jps工具查找ID.


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-273986-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    • 小悟
      小悟

      台湾如果敢宣布独立

    • 曹振宇
      曹振宇

      一直就比大陆人有更高品质的生活和更公平法治的社会环境

    热点图片
    拼命载入中...