Java面试知识点6

1.并行是什么意思?并行和并发有什么区别?

并行:指两个或两个以上事件或活动在同一时刻发生;如多个任务在多个CPU或CPU的多个核上同时执行,不存在CPU资源的竞争,等待行为。

区别

  1. 并行是指多个时间在同一个时刻发生;并发指在某时刻只有一个事件在发生,某个时间段由于CPU交替执行,可以发生多个事件;
  2. 并行没有对CPU资源的抢占;并发执行的线程需要对CPU资源进行抢占;
  3. 并行执行的线程之间不存在切换, 并发操作系统会根据任务调度系统给线程分配线程的CPU执行时间,线程的执行会进行切换;

Java中的多线程:

  1. 通过JDK中的java.lang.Thread可以实现多线程。
  2. Java中多线程运行的程序可能是并发也可能是并行,取决于操作系统对线程的调度和计算机硬件资源(CPU 的个数和CPU的核数);
  3. CPU资源比较充足时,多线程被分配到不同的CPU资源上,即并行;CPU资源紧缺时,多线程可能分配到同一个CPU的某个核上执行,即并发

2.什么是线程,进程?为什么要有线程?线程和进程的区别?

进程

  1. 程序执行时的一个实例;
  2. 每个进程都有独立的内存地址空间;
  3. 系统进行资源分配和调度的基本单位;
  4. 进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享,进程创建时分配,主要存放new创建的对象实例;
  5. 进程里的方法区,是用来存放进程中的代码片段,是线程共享的;
  6. 在多线程OS中,进程不是一个可执行的实体,即一个进程至少创建一个线程去执行代码;

线程

  1. 进程中的一个实体;
  2. 进程的一个执行路径;
  3. CPU调度和分派的基本单位
  4. 线程本身是不会独立存在的;
  5. 当前线程CPU时间片用完后,会让出CPU等下次轮到自己的时候在执行;
  6. 系统不会为线程分配内存,线程组之间只能共享所属进程的资源;
  7. 线程只拥有在运行中必不可少的资源(如程序计数器,栈);
  8. 线程里的程序计数器就是为了记录该线程让出CPU时候的执行地址,待再次分配到时间片时候就可以从自己私有的计数器指定地址继续执行;
  9. 每个线程都有自己的栈资源,用于存储该线程的局部变量和调用栈帧,其他线程无权访问;

为什么要有线程:每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大,请求响应效率低,因此操作系统引进线程。

关系

  1. 一个程序至少有一个进程,一个进程至少有一个线程,进程中的多个线程都是共享进程的资源;
  2. Java中当我们 启动main函数时候就启动了一个JVM的进程,而main函数所在线程就是这个进程中的一个线程,也叫主线程;
  3. 一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程都有自己的程序计数器,栈区域。
    在这里插入图片描述
    区别
  4. 进程有独立的资源单位,线程只是一个进程的不同的执行路径;
  5. 线程拥有自己的堆栈和局部变量,线程之间没有独立的地址空间。

3.什么是守护线程

java线程分两种,守护线程和用户线程。

1.守护线程:守护线程就是程序运行的时候在后台提供一种通用服务的线程。所以用户线程停止,进程会停掉所有守护线程,退出程序;

2.Java中把线程设置为守护线程的方法:在start线程之前调用线程的setDaemon(true)方法。
注意:

  1. setDaemon(true)方法必须在start()之前设置,否则会抛出异常,该线程仍默认为用户线程,继续执行;
  2. 守护线程创建的线程也是守护线程;

3.守护线程不应该访问,写入持久化资源,如文件,数据库,因为它会在任何时候都被停止掉,导致资源未释放,数据写入中断等问题。

4.如何创建,启动Java线程?

Java中有4种常见的创建线程的方式
1.重写Thread类的run()方法

  1. new Thread对象,匿名重写run()方法
  2. 继承Thread对象,重写run()方法

2.实现Runnable接口,重写run()方法

  1. new Runnable对象,匿名重写run()方法
  2. 实现Runnable()接口,重写run()方法

3.实现Callable接口,使用FutureTask类创建线程

4.使用线程池创建、启动线程

5.什么是并发编程?为什么要用并发编程?缺点?

并发

1.在程序设计的基础上,希望通过某些机制让计算机可以在同一个时间段内,执行多个任务;
2.一个或多个物理CPU在多个程序之间多路复用,提高对计算机资源的利用率;
3.任务数多于CPU的核数,通过操作系统的任务调度算法,实现多个任务一起执行;
4.有多个线程在执行,计算机只有一个CPU,不可能真正同时运行多个线程,操作系统只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其他线程处于挂起状态
并发编程:用编程语言编写让计算机可以在同一个时间段内执行多个任务的程序。

为什么

1.硬件的单元计算能力提升受限;硬件上提高了CPU的核数和个数。并发编程可以提升CPU的计算能力和效率;
2.提升程序的性能,如:响应时间、吞吐量、计算机资源使用率等;
3.并发程序可以更好的处理复杂业务,对复杂业务进行多任务拆分,简化任务调度,同步执行任务;

缺点

1.Java中的线程对应是操作系统级别的线程,线程数量控制不好,频繁的创建、销毁线程和线程间的切换,比较消耗内存和时间;
2.容易带来线程安全问题,如线程的可见性,有序性,原子性问题,会导致程序出现的结果与预期结果不一致;
3.多线程容易造成死锁,活锁,线程饥饿等问题。此类问题往往只能通过手动停止线程,甚至是进程才能解决,影响严重;
4.对编程人员的技术要求高,编写出正确的并发程序不容易;
5.并发程序容易出现问题,且难调试和排查,问题常常诡异地出现,又诡异地消失。

6.导致并发程序出问题的根本原因是什么?

CPU,内存,IO设备的读写速度差异巨大
表现为CPU的速度>内存的速度>IO设备的速度;
程序的性能瓶颈在于速度最慢的IO设备的读写,也就是当涉及到IO设备的读写,再怎么提升CPU和内存的速度也是起不到提升性能的作用;

为了更好地利用CPU的高性能:

  1. 计算机体系结构,给CPU增加了缓存,均衡CPU和内存的速度差异;
  2. 操作系统,增加了进程与现场,分时复用CPU,均衡CPU和IO设备的速度差异;
  3. 编译器,增加了指令执行重排序,更好地利用缓存,提高程序的执行速度;

基于以上优化给并发编程带来了三个问题:

  1. CPU缓存,在多核CPU的情况下,带来了可见性问题;可见性:一个线程对共享变量的修改,另一个线程能够立刻看到修改后的值,可以通过volatile,synchronized,locl接口,Atomic类型保障可见性;
  2. .操作系统对当前执行线程的切换,带来了原子性问题;原子性:一个或多个指令在CPU执行的过程中不被中断的特性;举例:一个线程在修改某个值时,另外的线程随后启动也修改该值,如一个线程+1000,另外一个也是+1000,但是第一个在加到一半时另外一个启程启动了,导致没加到2000;
  3. 编译器指令重排优化,带来了有序性问题:有序性:程序按照代码执行的先后顺序。

7.Java程序中怎么保证多线程的运行安全?

线程的安全性问题体现在:
1.原子性:一个或多个操作在CPU执行的过程中不被中断的特性;
2.可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到;
3.有序性:程序执行的顺序按照代码的先后顺序执行;

导致原因:

  1. 缓存导致的可见性问题;
  2. 编程切换带来的原子性问题;
  3. 编译优化带来的有序性问题。

解决办法:

  1. JDK Atomic开头的元子类,synchronized,LOCK,可以解决原子性问题;
  2. synchronized,volatile,LOCK可以解决可见性问题;
  3. Happens-Before规则可以解决有序性问题;

8.如何停止一个线程?

终止线程有两种情况:

  1. 线程的任务执行完成;
  2. 线程在执行任务过程中发生异常;

这两者属于线程自行终止,如何让线程A把线程B终止呢?

Java中Thread类有一个stop()方法,可以终止线程,不过该方法会让线程直接终止,在执行的任务立即终止,未执行的任务无法反馈,所以stop()方法已经不建议使用了;
线程只有从runnable状态(可运行/运行状态)才能进入termintaed状态(终止状态),如果线程处于blocked,waiting,timed_waiting状态(休眠状态),就需要通过Thread类的interupt()方法,让线程从休眠状态进入runnable状态,从而结束线程;当线程进入runnable状态之后,通过设置一个标识位,线程在合适的实际,检查该标识位,发现符合终止条件,自动退出run()方法,从而结束线程。

9.什么是线程池?为什么用它?线程池有哪些状态

线程池:线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会销毁,
而是在线程池中等待下一个任务。

原因:因为Java中创建一个线程,需要调用操作系统内核的API,操作系统要为线程分配一系列的资源,成本很高,所以线程是一个重量级对象,应该避免频繁创建和销毁。使用线程池就能很好避免频繁创建和销毁。

5种状态:1.RUNNABLE,SHUTDOWN,STOP,TIDYING,TERMINATED;

  1. RUNNABLE:线程池一旦创建,就处于RUNNABLE状态,任务数为0,能够接受到新任务,对已排列的任务进行处理;
  2. SHUTDOWN:不接受新任务,但能够处理已排队的任务。调用线程池的shutdown()方法,线程池由RUNNABLE->SHUTDOWN;
  3. STOP:不接受新任务,不处理已排队的任务,并且会中断正在处理的任务,调用线程池的shutdownNow()方法,线程池由(RUNNABLE或SHUTDOWN)转变为STOP;
  4. TIDYING:1.SHUTDOWN状态下,任务数为0,其他所有任务已终止,线程池会变为TIDYING状态,会执行terminated()方法。线程池中的terminated()方法是空实现,可以重写该方法进行相应的处理;2.线程池在SHUTDOWN状态,任务队列为空且执行中任务为空,线程池就会由SHUTDOWN->TIDYING状态 ;3.线程池在STOP状态,线程池彻底终止,线程池中执行中任务为空时,就会由STOP->TIDYING状态;
  5. TERMINATED:线程池彻底终止,线程池在TIDYING状态执行terminated()方法由TIDYING->TERMINATED状态。
    在这里插入图片描述

10.如何停止一个线程池?

1.shutdown()方法,有序关闭,已提交的任务继续执行,不接受新任务;

2.shutdownNow(),尝试停止所有正在执行的任务,停止等待执行的任务,并返回等待执行的任务列表;

3.awaitTermination(long timeout,TimeUnit unit),收到关闭请求后,所有任务执行完成,超时,线程被打断,阻塞直到三种情况任意一种发生,参数可以设置超时时间与超时单位,线程池关闭返回true,超过设置时间未关闭,返回false;
注意:

  1. 调用shutdown()和shutdownNow()方法关闭线程池,线程池都无法接收新的任务;
  2. shutdown()方法会继续执行正在执行未完成的任务,shutdownNow()方法会尝试停止所有正在执行的任务;

3.shutdown()方法没有返回值;shutdownNow()方法返回等待执行的任务列表;

4.awaitTermination(long timeout,TimeUnit unit)方法可以获取线程池是否已经关闭,需要配合shutdown()使用;

5.shutdownNow()不一定能够立马结束线程池,该方法会尝试停止所有正在执行的任务,通过调用Thread.interrupt()方法来实现的,如果 线程没有sleep(),wait(),Condition,定时锁等应用,interrupt()方法是无法中断当前的线程的。

11.synchronized关键字的作用是什么?volatile关键字的作用是什么?synchronized和volatile的区别是什么?

Java中关键字synchronized表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程;

作用:

  1. 确保线程互斥地访问同步代码;
  2. 保证共享变量的修改能够及时可见;
  3. 有效解决重排序问题;

用法:1.修饰普通方法;2.修饰静态方法;3.指定对象,修饰代码块;

特点:

  1. 阻塞未获取到锁,竞争同一个对象锁的线程;
  2. 获取锁无法设置超时;
  3. 无法实现公平锁;
  4. 控制等待和唤醒需要结合加锁对象的wait()和notify(),notifyAll();
  5. 锁的功能是JVM层面实现的;6.在加锁代码块执行完或者出现异常,自动释放锁;

原理:
1.同步代码块是通过monitorenter和monitorexit指令获取线程的执行权;
2.同步方法通过加ACC_SYNCHRONZIED标识实现线程的执行权的控制;

Java中的volatile关键字是一个类型修饰符,在JDK1.5后,对其语义进行了加强

  1. 保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了共享变量的值,共享变量修改后的值对其他线程立即可见;
  2. 通过禁止编译器、CPU指令重排序和部分happens-befor规则,解决有序性问题;

volatile可见性的实现:

  1. 在生成汇编代码指令时会在volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令;
  2. Lock前缀的指令会引起CPU缓存写回内存;
  3. 一个CPU的缓存回写到内存会导致其他CPU缓存了该内存地址的数据无效;
  4. volatile变量通过缓存一致性协议保证每个线程获得最新值;
  5. 缓存一致性协议保证每个CPU通过嗅探在总线上传播的数据来检查自己缓存的值是不是修改;
  6. 当CPU发现自己缓存行对应的内存地址被修改,会将当前CPU的缓存行设置成无效状态,重新从内存中把数据读到CPU缓存;

区别

  1. synchronized可以作用于变量、方法、对象;volatile只能作用于变量;
  2. synchronized可以保证线程间的有序性、原子性和可见性;volatile只保证了可见性和有序性,无法保证原子性;
  3. synchronized线程阻塞,volatile线程不阻塞;
  4. volatile本质是告诉jvm当前变量在寄存器中的值是不安全的,需要从内存中读取;synchronized则是锁定当前变量,只有当前线程可以访问到该变量其他线程被阻塞;
  5. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化;

12.Java中的锁是什么?

在并发编程中,经常遇到多个线程访问同一个共享变量。当同时对共享变量进行写操作时,就会产生数据不一致的情况,为了解决这个问题,在JDK1.5前,使用synchronized关键字,拿到Java对象的锁,保护锁定的代码块。JVM保证同一时刻只有一个线程可以拿到这个Java对象的锁,执行对应的代码块。在JDK1.5后,引入了并发工具 包java.util.concurrent.locks.Lock, 让锁的功能更丰富。

常见的锁

  1. synchronized:关键字锁定代码库 ;
  2. 可重入锁:java.util.concurrent.lock.ReentrantLock;
  3. 可重复读写锁:java.util.concurrent.lock.ReentrantReadWriteLock;

Java中不同维度的锁分类

  1. 可重入锁:指在同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁,JDK中基本都是可重入锁,避免死锁的发生;
  2. 公平锁/非公平锁:1.公平锁:指多个线程按照申请锁的顺序来获取锁;2.非公平锁:指多个线程获取锁的顺序不是按照申请锁的顺序,有可能后申请的线程先后的锁;
  3. 独享锁/共享锁:1.独享锁:指锁一次只能被一个线程所持有;2.共享锁:指锁可被多个线程所持有;
  4. 悲观锁/乐观锁:1.悲观锁:一律会对代码块进行加锁;2.乐观锁:默认不会进行并发修改,通常采用CAS算法不断尝试更新(悲观锁适合写操作较多的场景,乐观锁适合读操作较多的场景);
  5. 粗粒度锁/细粒度锁:1.粗粒度锁:就是把执行的代码块都锁定;2.细粒度锁:就是锁住尽可能小的代码块;
  6. 偏向锁/轻量级锁/重量级锁:1.JDK1.5后新增锁的升级机制,提升性能;2.通过synchronized加锁后,一段同步代码一直被同一个线程所访问,那么该线程获取的就是偏向锁;3.偏向锁被一个其他线程所访问时,Java对象的偏向锁就会升级为轻量锁;4.再有其他线程会以自旋的形式尝试获取锁,不会阻塞,自旋一定次数仍然未获取到锁,就会膨胀为重量级锁;
  7. 自旋锁:就是尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处就是减少线程上下文切换的消耗,缺点是循环占有,浪费CPU资源。

13.什么是活锁和饥饿?

活锁:任务没有阻塞,由于某些条件没有满足,导致一直重复尝试-失败-尝试-失败的过程。处于活锁的实体是在不断的改变状态,活锁有可能自行解开(死锁是大家都拿不到资源都占有着对方的资源,活锁是拿到资源却又相互释放不执行);

解决活锁的一个简单方法就是在下一次尝试获取资源之前,随机休眠一小段时间;

饥饿:一个线程因为CPU时间全部被其他线程抢占而得不到CPU运行时间,导致线程无法执行;

产生饥饿原因:

  1. 优先级线程吞噬所有低优先级线程的CPU时间;
  2. 其他线程总是能在它之前持续地对该同步块进行访问,线程被永久堵塞在一个等待进入同步块;
  3. 其他线程总是抢先被持续地唤醒,线程一直在等待被唤醒;

14.什么是死锁?如何避免死锁?

线程死锁是指由两个或两个以上线程相互持有对方所需要的资源,导致这些线程一直处于等待其他线程释放资源的状态,无法继续执行,如果线程都不主动释放所占有的资源,将产生死锁。当线程处于这种僵持状态,若无外力,他们都将无法再向前推进。

产生原因

  1. 持有系统不可剥夺资源,去竞争其他已被占用的系统不可剥夺资源,形成程序僵死的竞争关系。
  2. 持有资源的锁,去竞争锁已被占用的其他资源,形成程序僵死的竞争关系
  3. 信号量使用不当。

死锁发生的条件

  1. 互斥,共享资源只能被一个线程占有;
  2. 占有且等待;
  3. 不可抢占;
  4. 循环等待;

避免死锁方法:只要破坏以上4个中的一个条件就可以避免死锁,互斥是不能破坏的,因为加锁就是为了保证互斥;

  1. 一次性申请所有资源,破坏‘占有且等待’条件;
  2. 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它所占有的资源,破坏‘不可抢占’条件;
  3. 按序申请资源,破坏‘循环等待’条件;

14.sleep()和wait()有什么区别?

1.sleep()是Thread类的静态本地方法,wait()是Object类的成员本地方法;

2.sleep()方法可以在任何地方使用,wait()方法则只能在同步方法或同步代码块中使用,否则抛出异常;

3.sleep()会休眠当前线程指定时间,释放CPU资源,不释放对象锁,休眠时间到自动苏醒继续执行;wait()方法放弃持有的对象,进入等待队列,当该对象被调用notify()/notifyAll()方法后才有机会竞争获取对象锁,进入运行状态;

4.JDK1.8 sleep() wait()均需要捕获InterruptedException异常;

15.Runnable和Callable有什么区别?

1.Runnable接口run方法无返回值,Callable接口call方法有返回值,支持泛型;

2.Runnable接口run方法只能抛出运行时异常,且无法捕获处理;Callable接口call方法允许抛出异常,可以获取异常信息;

16.notify()和notifyAll()区别?

等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到该对象的等待池,等待池中的线程不会去竞争该对象的锁

锁池:只有获取了对象的锁,线程才能执行对象的synchronized代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待。

区别:notify()方法随机唤醒对象的等待池的一个线程,进入锁池;notifyAll()唤醒对象的等待池中所有线程,进入锁池;

17.ThreadLocal有什么作用?有哪些使用场景?

作用:ThreadLocal是线程本地存储,在每个线程中都创建了一个ThreadLocalMap对象,每个线程都可以访问自己内部ThreadLocalMap对象内的value。通过这种方式,避免资源在多线程间共享。

场景:为每个线程分配一个JDBC连接Connection,这样就可以保障每个线程都在各自的Connection上进行数据库的操作,不会出现A线程关了B线程正使用的Connection,还有Session管理等问题;

18.乐观锁和悲观锁是什么?

悲观锁:线程每次在处理共享数据时都会上锁,其他线程想处理数据就会阻塞直到获得锁;

乐观锁:线程每次处理共享数据时都不会上锁,在更新时会通过数据的版本号等机制判断其他线程有没有更新数据,乐观锁适合读多写少的应用场景;

优缺点:

  1. 乐观锁适用于读多写少的场景,可以省去频繁的加锁,释放锁的开销,提高吞吐量
  2. 在写比较多的场景,乐观锁会因为版本号不一致,不断尝试更新,尝试大量自旋,消耗CPU,消耗性能,这种情况下适合用悲观锁

19.使用对象的wait()方法需要注意什么?

1.wait()方法是线程间通信的方法之一;

2.必须在synchronized方法或synchronized修饰的代码块中使用,否则抛出IllegalMonitorStateException;

3.只能在加锁的对象调用wait()方法;

4.加锁的对象调用wait()方法后,线程进入等待状态,直到在加锁的对象上调用notify()或者notifyAll()方法来唤醒之前进入等待的线程;

20.那种方式不会使线程进入阻塞状态?如何保证多个线程同时启动

D
A.sleep():进入休眠;
B.suspend():挂起线程,线程作废;
C.wait():进入等待;
D.yield():进入就绪;

多个线程启动:
可以wait(),notify()使用;也可以使用发令枪CountDownLatch实现;

21…volatile关键字能否保证线程安全?

单纯使用volatile关键字是不能保证线程安全的

  1. volatile只提供了一种弱的同步机制,用来确保将变量的更新操作通知到其他线程;
  2. volatile语义是禁止CPU缓存,直接从主内存读、写变量。表现为:更新volatile变量时,JVM会把线程对应的本地内存中的共享变量值刷新到主内存中;读volatile变量时,JVM会把线程对应额本地内存设置为无效,直接从主内存中读取共享变量;
  3. 当把变量声明为volatile类型后,JVM增加内存屏障,禁止CPU进行指令重排序;

22.创建进程需要?

BC
A、由调度程序为进程分配CPU;
B、建立一个进程控制块;
C、分配内存;
D、分配文件描述符;

分析:任务调度的单位是线程,如果未涉及到对文件的操作,可能不会分配分文件描述符。

23…sleep()和yield()区别?

1.sleep()方法给其他线程运行机会时不考虑线程的优先级;yield()方法只会给相同优先级或更高优先级的线程运行的机会;

2.线程执行sleep()方法后进入阻塞状态;线程执行yield()方法进入就绪状态,可能马上又得到执行;

3.sleep()方法声明抛出InterruptedException;yield()方法没有声明抛出异常;

4.sleep()方法需要指定时间参数;yield()方法让出CPU的执行权时间由jvm控制;

24.线程的run()方法和start()方法有什么区别?

1.启动一个线程需要调用Thread对象的start()方法;

2.调用线程的start()方法后,线程处于可运行状态,此时它可以由JVM调度并执行,这并不意味着线程会立即运行;

3.run()方法是线程运行时由JVM回调的方法,无需手动写代码调用;

4.直接调用线程的run()方法,相当于在调用线程里继续调用方法,并未启动一个新的线程

25.说说对于synchronized同步锁的理解?

1.每个Java对象都有一个内置锁;

2.线程运行到非静态的synchronized同步方法上时,自动获得实例对象的锁;

3.持有对象锁的线程才能运行synchronized同步方法或代码块时;

4.一个对象只有一个锁;

5.一个线程获得该锁,其他线程就无法获取锁,直到第一个线程释放锁。任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放;

6.释放锁是指持有锁线程退出了synchronized同步方法或同步块;

7.类可以同时拥有同步和非同步方法;

8.只有同步方法,没有同步变量和类;

9.在加锁时,要明确需要加锁对象;

10.线程可以获得多个锁 ;

11.同步应该尽量缩小范围。

26.Java中实现线程通信方式有哪些?

1.对象的wait(long timeout),wait(long timeout,int nanos),wait()方法,组合对象的notify(),notifyAll();

2.显示锁:Lock.newCondition(),Condition await系列方法、Condition signal()、signalAll();

3.信号量:Semaphore acquire系列方法,release()系列方法;

27.请解释一下Java多线程回调是什么意思?

所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。

28.JAVA中如何确保N个线程可以访问N个资源,但同时又不导致死锁?

死锁发生的四个必要条件是,资源互斥,请求与保持条件,不剥夺条件,以及循环等待条件。避免死锁最常用的方法就是指定线程获取锁的顺序,这样可以避免循环等待的发生。

猜你喜欢

转载自blog.csdn.net/qq_42748009/article/details/112212663