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

Java多线程面试问答(2019)

电脑杂谈  发布时间:2020-05-29 02:22:13  来源:网络整理

java多线程避免死锁_避免线程死锁_java线程死锁例子

原始博客地址:

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

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

在语言级别有三种方法. java.lang.Thread类的一个实例是线程,但需要调用java.lang.Runnable接口才能执行. 由于线程类本身是被称为Runnable的接口,因此您可以继承java.lang.Thread类或直接调用Runnable的接口以重复编写run()方法来实现线程. 第三种类型实现Callable <>接口并覆盖call方法.

此问题是上一个问题的后续. 每个人都知道我们可以通过继承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多线程避免死锁,也称为管道锁定规则.

上一次对volatile的写操作也称为下一个volatile读操作之前的volatile变量规则.

程的start()调用之后也必须调用线程内的任何操作,也称为线程启动规则.

线程的所有操作将程终止之前终止.

必须在对象的构造完成后调用该对象的完成操作,也称为对象的完成规则.

可运输性

java线程死锁例子_java多线程避免死锁_避免线程死锁

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

volatile是一个特殊的修饰符java多线程避免死锁,只有成员变量可以使用它. 这样可以确保对该变量进行操作的不同线程的可见性,即一个线程修改了变量的值,而其他线程可以立即看到新值.

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

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

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线程间通信”(涉及两个线程之间的对象共享)使用wait和notify方法实现生产者使用者模型.

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

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

ThreadLocal主要用于解决多线程数据并发导致的不一致问题. ThreadLocal在每个线程中提供并发访问的数据的副本,并通过访问副本来经营业务. 这导致内存消耗,从而大大降低了线程同步的性能成本和线程并发控制的复杂性. 度.

ThreadLocal不能使用原子类型,只能使用对象类型. ThreadLocal的使用比同步要简单得多.

ThreadLocal和Synchonized用于解决多线程并发访问. 但是ThreadLocal与同步本质上不同. 同步是一种使用锁的机制,因此变量或代码块一次只能由一个线程访问. ThreadLocal为每个线程提供变量的副本,以便每个线程在特定时间不访问同一对象,从而隔离了多个线程的数据共享. 同步恰好相反. 它用于在多个线程之间进行通信时进行数据共享.

并发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多线程中的死锁

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

完全互斥的条件: 资源一次只能由一个进程使用.

请求和保留条件: 当一个进程被请求资源阻塞时,它将保留其已获取的资源.

无剥夺条件: 在使用结束之前,不能强行剥夺该进程已获取的资源.

循环等待条件: 一个在多个过程之间形成循环等待资源关系的过程.

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


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

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

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