Java面试问题总结带答案(多线程)

问题总结(均在网上搜索和书本摘抄所得,如若侵权请联系立即删除)

多线程

开启线程的方式

在这里插入图片描述

说说进程,线程,协程之间的区别

1.进程:
通俗理解一个运行起来的程序或者软件叫做进程。
进程是操作系统资源分配的基本单位。
默认情况下一个进程会提供一个线程(主线程),线程依附在进程里,一个进程可创建多个线程。

2.进程和线程的对比:
进程是操作系统资源分配的基本单位,线程是cpu调度的基本单位。
线程依附于进程存在,没有进程就没有线程,进程索要资源,然后让线程执行相应的代码。
一个进程可创建多个线程。
进程之间不共享全局变量,线程之间共享,但是要注意资源竞争的问题,解决办法(互斥锁或者线程同步)。
多进程开发比单进程多线程开发稳定性要强,因为某一个进程挂了不会影响其他进程运行。
多进程开发比单进程多线程开发资源消耗大,因为每启动一个进程都需要向操作系统索要运行资源,但是线程可以共享进程中的资源,极大的提高了程序的运行效率。

3.进程,线程,协程对比:
先有进程,进程里提供线程,线程里包含多个协程。
进程是操作系统资源分配的基本单位,默认提供一个线程去执行代码。
线程是cpu调度的基本单位,cpu调度那个线程,那个线程去执行对应的代码。
进程之间不共享全局变量, 线程之间共享全局变量,但是要注意点资源竞争数据错误的问题,解决办法:互斥锁。
多进程开发比单进程多线程开发稳定性要强,但是资源开销要大。
线程之间执行是无序的,协程之间按照一定顺序交替执行的。
协程主要用在网络爬虫,网络请求,以后大家主要线程或者协程完成多任务。
开辟协程大概需要5k,开辟线程大概需要512k, 开辟进程需要资源更多。

为什么要有线程,而不是仅仅用进程?

进程缺点:

  • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
  • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

线程优点:

  • 提高进程的并发度
  • 可以有效地利用多处理器和多核计算机、一个进程的线程分散到多核种
    详细点这里

线程之间是如何通信的?

1.volatile
2.等待/通知机制
3.join方式
4.(threadLocal、管道、队列)

什么是Daemon线程?它有什么意义?

Daemon线程(守护线程)是运行在后台的一种特殊线程,独立于控制终端并且周期性地执行某种任务或等待处理某些已发生的事件。也就是说,守护线程不依赖于终端,但是依赖于JVM,与JVM“同生共死”。在JVM中的所有线程都是守护线程时,JVM就可以退出了,如果还有一个或一个以上的非守护线程,则JVM不会退出。
创建:
将一个用户线程设置为守护线程的方法是在线程对象创建之前用线程对象的setDaemon(true)来设置。
在后台守护线程中定义的线程也是后台守护线程。
意义:
为用户线程提供公共服务,在没有用户线程可服务时会自动离开。
守护线程的优先级较低,用于为系统中的其他对象和线程提供服务。
后台守护线程是JVM级别的,比如垃圾回收线程就是一个经典的守护线程

在java中守护线程和本地线程区别?

唯一的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。

什么是可重入锁(ReentrantLock)?

独占锁:指该锁在同一时刻只能被一个线程获取,而获取锁的其他线程只能在同步队列中等待;
可重入锁:指该锁能够支持一个线程对同一个资源执行多次加锁操作。可重入锁也叫作递归锁,指在同一线程中,在外层函数获取到该锁之后,内层的递归函数仍然可以继续获取该锁。在Java环境下,ReentrantLock和synchronized都是可重入锁。
ReentrantLock:

  • ReentrantLock继承了Lock接口并实现了在接口中定义的方法,是一个可重入的独占锁。ReentrantLock通过自定义队列同步器(Abstract Queued Sychronized,AQS)来实现锁的获取与释放。
  • ReentrantLock有显式的操作过程,何时加锁、何时释放锁都在程序员的控制之下。具体的使用流程是定义一个ReentrantLock,在需要加锁的地方通过lock方法加锁,等资源使用完成后再通过unlock方法释放锁。

什么是线程组,为什么在Java中不推荐使用?

  • 线程组ThreadGroup对象中的stop,resume,suspend会导致安全问题,主要是死锁问题,已经被官方废弃。
  • 线程组ThreadGroup不是线程安全的,在使用过程中不能及时获取安全的信息。

乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

  • 乐观锁采用乐观的思想处理数据,在每次读取数据时都认为别人不会修改该数据,所以不会上锁,但在更新时会判断在此期间别人有没有更新该数据,通常采用在写时先读出当前版本号然后加锁的方法。时先读出当前版本号然后加锁的方法。具体过程为:比较当前版本号与上一次的版本号,如果版本号一致,则更新,如果版本号不一致,则重复进行读、比较、写操作。
    Java中的乐观锁大部分是通过CAS(Compare And Swap,比较和交换)操作实现的,CAS是一种原子更新操作,在对数据操作之前首先会比较当前值跟传入的值是否一样,如果一样则更新,否则不执行更新操作,直接返回失败状态。
  • 悲观锁采用悲观思想处理数据,在每次读取数据时都认为别人会修改数据,所以每次在读写数据时都会上锁,这样别人想读写这个数据时就会阻塞、等待直到拿到锁。
    Java中的悲观锁大部分基于AQS(Abstract Queued Synchronized,抽象的队列同步器)架构实现。AQS定义了一套多线程访问共享资源的同步框架,许多同步类的实现都依赖于它,例如常用的Synchronized、ReentrantLock、Semaphore、CountDownLatch等。该框架下的锁会先尝试以CAS乐观锁去获取锁,如果获取不到,则会转为悲观锁(如RetreenLock)。

Java中用到的线程调度算法是什么?

  • 优先调度算法
    • 先来先服务调度算法
    • 短作业优先调度算法
  • 高优先权优先调度算法
    • 非抢占式优先调度算法
      非抢占式优先调度算法在每次调度时都从队列中选择一个或多个优先权最高的作业,为其分配资源、创建进程和放入就绪队列。
    • 抢占式优先调度算法
      抢占式优先调度算法首先把CPU资源分配给优先权最高的任务并运行,但如果在运行过程中出现比当前运行任务优先权更高的任务,调度算法就会暂停运行该任务并回收CPU资源,为其分配新的优先权更高的任务。
    • 高响应比优先调度算法
      高响应比优先调度算法使用了动态优先权的概念,即任务的执行时间越短,其优先权越高,任务的等待时间越长,优先权越高,这样既保障了快速、并发地执行短作业,也保障了优先权低但长时间等待的任务也有被调度的可能性。

同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。
同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。
同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样从侧面来说也可以避免死锁。

run()和start()方法区别

start方法与run方法的区别如下。
◎ start方法用于启动线程,真正实现了多线程运行。在调用了线程的start方法后,线程会在后台执行,无须等待run方法体的代码执行完毕,就可以继续执行下面的代码。
◎ 在通过调用Thread类的start方法启动一个线程时,此线程处于就绪状态,并没有运行。
◎ run方法也叫作线程体,包含了要执行的线程的逻辑代码,在调用run方法后,线程就进入运行状态,开始运行run方法中的代码。在run方法运行结束后,该线程终止,CPU再调度其他线程。

如何控制某个方法允许并发访问线程的个数?

想控制允许访问线程的个数就要使用到Semaphore。
Semaphore是一种基于计数的信号量,在定义信号量对象时可以设定一个阈值,基于该阈值,多个线程竞争获取许可信号,线程竞争到许可信号后开始执行具体的业务逻辑,业务逻辑在执行完成后释放该许可信号。在许可信号的竞争队列超过阈值后,新加入的申请许可信号的线程将被阻塞,直到有其他许可信号被释放。

Semaphore有两个方法semaphore.acquire() 和semaphore.release()。

semaphore.acquire() :请求一个信号量,这时候的信号量个数-1(一旦没有可使用的信号量,也即信号量个数变为负数时,再次请求的时候就会阻塞,直到其他线程释放了信号量)。
semaphore.release() 释放一个信号量,此时信号量个数+1。

在Java中wait和seelp方法的不同

sleep方法与wait方法的区别如下。
◎ sleep方法属于Thread类,wait方法则属于Object类。
◎ sleep方法暂停执行指定的时间,让出CPU 给其他线程,但其监控状态依然保持,在指定的时间过后又会自动恢复运行状态。
◎ 在调用sleep方法的过程中,线程不会释放对象锁。
◎ 在调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁池,只有针对此对象调用notify方法后,该线程才能进入对象锁池准备获取对象锁,并进入运行状态。

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

调用yield方法会使当前线程让出(释放)CPU执行时间片,与其他线程一起重新竞争CPU时间片。在一般情况下,优先级高的线程更有可能竞争到CPU时间片,但这不是绝对的,有的操作系统对线程的优先级并不敏感。

什么是不可变对象,它对写并发应用有什么帮助?

不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。

不可变对象的类即为不可变类(Immutable Class)。Java平台类库中包含许多不可变类,如String、基本类型的包装类、BigInteger和BigDecimal等。

不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然它们的状态无法修改,这些常量永远不会变。

不可变对象永远是线程安全的。

只有满足如下状态,一个对象才是不可变的;

  • 它的状态不能在创建后再被修改;
  • 所有域都是final类型;并且,它被正确创建(创建期间没有发生this引用的逸出)。

谈谈wait/notify关键字的理解

(1)如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

(2)当有线程调用了对象的notifyAll()方法(唤醒所有wait线程)或notify()方法(只随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。

(3)优先级高的线程竞争到对象锁的概率大,假若某

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值