并发
-
- 1.并发编程的优缺点?
- 2.并发编程三要素?
- 3.什么叫指令重排?
- 4.如何避免指令重排?
- 5.并发?并行?串行?
- 6.线程和进程的概念和区别?
- 7.什么是上下文切换?
- 8.守护线程和用户线程的定义?
- 9.什么是线程死锁?
- 10.形成死锁的四个条件?
- 11.怎么避免死锁?
- 12.创建线程的四种方式?
- 13.runable和callable区别?
- 14.run()和start()的区别?
- 15.什么是futureTask?
- 16.为什么我们调用start()方法会执行run()方法,为什么我们不能直接调用run()方法?
- 17.线程生命周期及五种状态的转换?
- 18.线程调度的几种模型?
- 19.线程调度策略?
- 20.什么是线程调度器和时间分片?
- 21.wait、sleep、yield区别?notify、notifyAll区别?
- 22.sleep、yield为什么是静态的?
- 23.如何调用wait()?使用if块还是循环块?
- 24.为什么线程通信方法wait()、notify()、notifyAll()定义在Object中?
- 25.为什么wait()、notify()、notifyAll()必须在同步方法或者同步块中被调用?
- 26.如何停止一个正在运行的线程?
- 27.interrupt、interrupted和isInterrupt方法的区别?
- 28.怎么唤醒阻塞线程?
- 29.什么是阻塞式方法?
- 30.实现线程同步的方法?
- 31.同步方法和同步块是什么?
- 32.线程池的工作原理?
- 33.创建线程池都有哪些方式?
- 34.线程池常用的几个参数?
- 35.线程池的拒绝策略有哪些?
- 36.线程池都有哪些状态?
- 37.线程池中submit()和execute()的区别?
- 38.当你提交任务时,线程池队列已满,这时会发生什么?
- 39.synchronized使用方式?
- 40.synchronized的锁升级的过程
- 41.synchronized底层原理?
- 42.什么是自旋?
- 43.synchronized可重入的原理?
- 44.synchronized和volicate区别?
- 45.Lock和synchronized区别?(面试问过)
- 46.synchronized和ReentrantLock可重入锁的区别?
- 47. ReentrantLock是什么?
- 48.synchronized为什么不能集群操作?如果想集群操作用什么?
- 49.线程池用完扔回线程池是什么状态?
- 50.CAS和ABA的问题?
- 51.线程池的线程数是怎么确定的?
- 52.ThredLocal是什么?以及使用场景?
- 53.ThreadLocal的ThreadLocalMap是强引用还是弱引用?
- 53.什么是临界区?
- 53.ab同时提交线程完成任何一个就去执行C,用什么来完成?
- 54.ab同时提交线程,需要判断结果,就去执行C,用什么来完成?
- 55.CountDownLatch是什么?
- 56.ExecutorService是什么?
- 55.AQS简单介绍一下?(面试)
- 55.CLH是什么?
- 55.什么是可重入锁?
- 55.怎么理解线程安全?
- 55.线程池的应用场景?
- 54.为什么要使用并发编程?
- 55.双重校验锁实现对象单例模式?
- 56.多线程的应用场景?
1.并发编程的优缺点?
优点:
- 充分利用多核CPU的计算能力,通过并发编程的形式可以将多核CPU的计算能力发挥到极致,性能得到提升;
- 方便业务拆分,提升系统并发能力和性能;
缺点:
- 内存泄漏;
- 线程安全问题;
- 复杂程度增加:比如死锁;
- 资源消耗增加:比如频繁的上下文切换也可能导致额外的性能开销;
2.并发编程三要素?
- 原子性:原子,就是一个不可再被分割的颗粒。原子性就是指一个或多个操作要么全部执行成功要么全部执行失败;
- 可见性:一个线程对共享变量的修改,另一个线程能够立刻看到;
- 有序性:程序执行的顺序按照代码先后顺序执行。(处理器可能会对指令进行重排序)
3.什么叫指令重排?
指令重排(也称为指令重排序)是指在程序执行过程中,指令的执行顺序可能与它们在代码中的顺序不一致的现象。编译器和处理器为了提高程序的执行效率,可能会根据一些规则和优化策略对指令进行重新排序。但是,这种重排序必须保证最终的执行结果与不进行重排时的执行结果保持一致,以确保程序的正确性;存在数据依赖关系的也不允许指令重排
指令重排主要是基于处理器的特性,如多级缓存、多核等,来优化指令的执行顺序。这种优化可以使程序在保证业务运行的同时,充分利用CPU的资源,发挥最大的性能。然而,指令重排也可能会导致线程安全问题,特别是在多线程环境下。因此,在编写并发程序时,需要特别注意指令重排的影响,并采取相应的措施来确保程序的正确性和性能。
4.如何避免指令重排?
- 使用volatile关键字:在Java中,volatile关键字可以确保多线程环境下变量的可见性和有序性。当一个变量被声明为volatile时,它会禁止指令重排,确保所有线程看到的变量值都是一致的。volatile关键字还可以防止JVM的指令重排优化,确保代码的执行顺序与预期一致。
- 使用synchronized关键字:synchronized关键字可以用来保证代码块或方法的原子性,即在同一时刻只能有一个线程执行被保护的代码。通过synchronized块或方法,可以确保指令按照预期的顺序执行,避免指令重排导致的线程安全问题。
- 使用Lock接口及其实现类:Java中的Lock接口及其实现类(如ReentrantLock)也可以用来控制并发访问,保证代码的正确执行顺序。与synchronized相比,Lock接口提供了更灵活的控制方式,可以更好地避免指令重排带来的问题。
- 避免使用final关键字修饰引用类型变量:在Java中,final关键字修饰的引用类型变量在初始化后不能被改变。但是,如果final变量指向的对象是可变的,那么其他线程仍然可以修改该对象的内容。因此,在使用final关键字时,需要特别注意避免指令重排导致的线程安全问题。
- 了解并遵循Happens-Before规则:Happens-Before规则是Java内存模型定义的一组规则,用于确定多线程环境中哪些操作是有序的。遵循这些规则可以确保指令按照预期的顺序执行,避免指令重排导致的线程安全问题。
5.并发?并行?串行?
- 并发:多个任务在同一个cpu上,按细分的时间片轮流执行,从逻辑上来看那些任务是同事执行的;(两个队列一台咖啡机)
- 并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的同时进行;(两个队列一两台咖啡机)
- 串行:有n个任务,由一个线程按顺序执行,犹豫任务,方法都在一个线程执行,所以不存在线程不安全情况,也就不存在临界区的问题;(一个队列一台咖啡机)
6.线程和进程的概念和区别?
进程:是操作系统资源分配的基本单位,一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程可以有多个线程。
线程:处理器任务调度和执行的基本单位,又叫做轻型进程;进程中的一个执行任务,负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据;
区别:
- 资源开销:进程是系统分配资源的基本单位,它拥有独立的内存空间和系统资源,因此创建和销毁一个进程需要较大的开销,包括内存分配、上下文切换等。而线程是进程内的一条执行路径,多个线程共享同一个进程的内存空间和资源,因此创建和销毁一个线程的开销相对较小。
- 执行方式:进程是独立执行的,拥有自己的地址空间和资源,相互之间通过进程间通信(IPC)进行交互。而线程是进程内的一条执行路径,多个线程之间共享进程的资源,因此它们之间的通信和同步更为直接和高效。
- 并发性:进程在并发执行时具有更高的稳定性,因为每个进程都是独立的执行单元,拥有自己的调度算法。而线程之间的调度和同步相对复杂,需要更多的注意,以避免出现竞态条件、死锁等问题。
- 独立性:进程是独立的,一个进程出现问题不会影响其他进程的执行。而线程是进程的一部分,一个线程的错误可能导致整个进程的崩溃。
7.什么是上下文切换?
CPU采取的策略是为每个线程分配时间片并轮训的形式,当前任务在执行完CPU时间片切换到另一个任务之前会保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态,任务从保存到再加载过程就是一次上下文切换。
上下文切换消耗大量的CPU时间,可能是操作系统中时间消耗最大的操作。
8.守护线程和用户线程的定义?
守护线程:运行在后台,为其他前台线程服务。也可以说守护线程是JVM中非守护线程的"佣人",一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作;
用户线程:运行在前台,执行具体任务,如程序的主线程,连接网络的子线程等都是用户线程。
守护线程不能依靠finally块的内容来确保执行关闭或清理资源的逻辑,因为用户线程结束,守护线程就跟着结束,所以守护线程中的finally语句块可能无法被执行;
9.什么是线程死锁?
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成一种阻塞的现象,若无外力作用,他们都将无法推进下去。
10.形成死锁的四个条件?
- 互斥条件:线程对于所分配到的资源具有排他性,即一个资源只能被一个线程占用,直到该线程释放;
- 请求与保持条件:一个线程因请求被占用资源而发生阻塞时,对已获得的资源保持不放;
- 不可剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完后才释放资源;
- 循环等待条件:当发生死锁,所等待的线程必定会形成一个环路,造成永久阻塞。
11.怎么避免死锁?
破坏产生死锁的四个条件中的其中一个:
- 破坏互斥条件:
- 破坏请求与保持条件:
- 破坏不可剥夺条件:
- 破坏循环等待条件:
12.创建线程的四种方式?
继承Thread类:
- 定义一个Thread类的子类,重写run方法,run方法里就是相关业务逻辑;
- 创建自定义的线程子类对象;
- 调用子类实例的start方法启动线程;
实现runable接口:
- 定义runnable接口实现类MyRunnable,并重新run方法;
- 创建MyRunnable实例myRunnable,以myRunnable作为target创建Thread对象,该Thread对象才是真正的线程对象;
- 调用线程对象的start方法;
实现callable接口:
- 创建实现callable接口的类myCallable;
- 以myCallable为参数创建FutureTask对象;
- 将FutureTask作为参数创建Thread对象;
- 调用线程对象的start方法;
使用Excutors工具类创建线程池:
Excutors提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
13.runable和callable区别?
相同点:
- 都是接口;
- 都可以编写多线程;
- 都采用Thread.start()启动线程;
不同点:
- Runnable接口run方法只能抛出异常,不能捕获异常,没有返回值;Callable接口call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果;
14.run()和start()的区别?
run()方法称为线程体,通过调用Thread类的start()方法来启动一个线程;run()可以重复调用,start()只能调用一次;
15.什么是futureTask?
表示一个异步运算的任务,里面可以传一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。只有当运算完成的时候才能取回结果,如果尚未完成运算,get方法将会阻塞。
16.为什么我们调用start()方法会执行run()方法,为什么我们不能直接调用run()方法?
首先,当你调用一个线程的 start() 方法时,Java虚拟机(JVM)会为这个线程创建一个新的调用栈,并将该线程标记为可运行状态。然后,JVM会调度这个线程执行,当线程获得CPU时间片时,就会执行该线程的 run() 方法。这种方式允许线程在操作系统级别进行调度,从而能够充分利用多核CPU和操作系统提供的线程调度机制,实现真正的并发执行。
而如果你直接调用 run() 方法,那么这个方法就会在当前的调用栈中执行,它只是一个普通的方法调用,并不会启动一个新的线程。也就是说,run() 方法会在当前线程中同步执行,不会创建新的线程,也就无法实现并发。
因此,start() 方法和 run() 方法的区别在于:start() 方法用于启动一个新的线程来执行 run() 方法,而 run() 方法本身只是一个普通的方法调用,不会创建新的线程。
17.线程生命周期及五种状态的转换?
18.线程调度的几种模型?
- 分时调度模型:这种模型让所有线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片。这种方式下,每个线程都会得到一定的执行时间,但也可能因为时间片过短而无法完成复杂的任务。
- 抢占式调度模型:这种模型优先让可运行池中优先级高的线程占用CPU。如果线程的优先级相同,那么就随机选择一个线程使其占用CPU。当线程丢失了CPU的使用权后,再随机选择其他线程获取CPU的使用权。这种方式下,优先级高的线程会获得更多的执行机会。
需要注意的是,线程的调度不是跨平台的,它不仅取决于JVM(Java虚拟机),还依赖操作系统。在Java中,抢占式调度模型被采用作为默认的线程调度模型。
19.线程调度策略?
线程调度器选择优先级最高的线程运行,但是遇到下面几种情况,就会终止线程的运行:
- 调用yield方法,让出cpu的占有权;
- 调用sleep方法使线程进入睡眠状态;
- 另一个更高优先级的线程出现;
- 在支持时间片的系统中,该线程的时间片用完。
20.什么是线程调度器和时间分片?
线程调度器:是一个操作系统服务,他负责为Runnable状态的线程分配CPU时间;
时间分片:是指将可用的CPU时间分配给Runnable线程的过程。
21.wait、sleep、yield区别?notify、notifyAll区别?
wait():使一个线程处于等待阻塞状态,并且释放所持对象的锁,Object类的方