Java面试题-Java多线程

Java面试题

Java多线程

1.进程与线程的区别是什么。

1.进程是程序在处理机上一次调度的过程,是动态的概念。
  线程是进程的一个实体。
2.进程是资源分配和系统调度的基本单位。
  线程是cpu调度的基本单位。
3.一个进程至少包含一个线程。线程的上下文切换速度要比进程快得多。

2.单线程与多线程是什么关系。

1.多线程是指在一个进程中,并发执行了多个线程,
  每个线程都实现了不同的功能
2.在单核CPU中,将CPU分为很小的时间片,
  在每一时刻只能有一个线程在执行,是一种微观上轮流占用CPU的机制。
  由于CPU轮询的速度非常快,所以看起来像是“同时”在执行一样
3.多线程会存在线程上下文切换,会导致程序执行速度变慢
4.多线程不会提高程序的执行速度,反而会降低速度。
  但是对于用户来说,可以减少用户的等待响应时间,提高了资源的利用效率

3.简述线程的几种状态。

1.新建(NEW)
  线程刚创建出来,还未调用start所处的状态。
2.运行(Runnable)
  线程正在运行或者正在等待cpu资源。
3.阻塞(Blocked)
  当线程准备进入synchronized修饰的方法或者代码块的时候,
  需要获取一个监视器锁,会使线程进入阻塞状态。
4.等待(waiting)
  该状态是因为调用了Object.wait()、Thread.join()、
  LockSupport.part()会处于等待状态。
5.TIMED_WAITING
  与WAITING的状态是一样的,只不过是定时的。
6.消亡(TERMINTED)
  线程执行完毕,就处于消亡状态。  
  
拓展:线程状态,BLOCKED 和 WAITING 有什么区别
     答案:这道题第一眼看上去有点懵,但是答案就在上面,就是34
          的区别。

4.sleep、wait、join、yield有什么区别。

1.sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,
  线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,
  等待CPU的到来。睡眠不释放锁(如果有的话)。
2.wait方法:是Object的方法,必须与synchronized关键字一起使用,
  线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。
  但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
3.join 方法:当前线程调用,则其它线程全部停止,
  等待当前线程执行完毕,接着执行。
4.yield 方法:该方法使得线程放弃当前分得的 CPU 时间。
  但是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。

5.线程的活性障碍。

常见的线程活性故障包括死锁,锁死,活锁与线程饥饿。

1.死锁(产生死锁有四个必要条件)
  1.资源互斥:一个资源每次只能被一个线程使用
  2.请求和保持:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  3.不可剥夺:线程已经获得的资源,在未使用完之前,不能强行剥夺
  4.循环等待:若干线程之间形成一种头尾相接的循环等待资源关系
拓展:怎么避免死锁
     答案:1.锁粗化:用来消除请求和保持条件。
          2.锁排序:将获取锁的进行排序,必须按顺序获取锁
            才能获取到资源。
            
2.线程锁死(有两个原因产生活锁)
  1.信号丢失:没有通知到线程,该线程一直处于阻塞的状态。
  2.监视器嵌套:一般都是因为要获取多重锁导致的。

3.活锁:线程处于可运行状态,但是任务一直无法进展,称为活锁。

4.线程饥饿:线程处于不可运行状态,因为获取不到某个资源。

总结:
1.线程饥饿发生时,如果线程处于可运行状态,也就是其一直在申请资源,
  那么就会转变为活锁
2.只要存在一个或多个线程因为获取不到其所需的资源而无法进展
  就是线程饥饿,所以线程死锁其实也算是线程饥饿

6.多线程的几种实现方式。

1.继承Thread类,重写run方法。
2.实现Runnable接口,重写run方法。
3.通过Callable和FutureTask创建线程。
4.通过线程池创建线程。

7.多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。

此问题的本质是保持顺序执行。可以使用executors

8.用过读写锁吗,原理是什么,一般在什么场景下用。

与传统锁不同的是读写锁的规则是可以共享读,但只能一个写,总结起来为: 读读不互
斥,读写互斥,写写互斥 ,而一般的独占锁是: 读读互斥,读写互斥,写写互斥 ,而
场景中往往 读远远大于写 ,读写锁就是为了这种优化而创建出来的一种机制。就是使
用在读多写少的场景下。

9.什么是CAS。

CAS(Compare And Swap 比较并且替换)是乐观锁的一种实现方式,是一种轻量级
锁,JUC 中很多工具类的实现就是基于 CAS 的。

10.CAS 是怎么实现线程安全的?

线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原
值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。

11.CAS存在什么问题?

1.当写入的时候,如果重新读取的值与原值永远不相同,那么就会永远
  循环,造成cpu资源浪费。
2.CAS还存在AB问题。
3.只能保证一个共享变量的原子操作。
  CAS操作单个共享变量的时候可以保证原子的操作,多个变量就不行了,JDK 5之后 
  AtomicReference可以用来保证对象之间的原子性,就可以把多个对象放入CAS中
  操作。

12.JUC包下的原子类也是通过CAS实现的吗。

是的,比如说AutomicInteger类中的incrementAndGet(),自增方法
就是通过CAS实现的。

13.乐观锁在项目开发过程中有涉及到吗。

有的。比如我们在很多订单表,流水表,为了防止并发问题,就会加入CAS的校验过程,
保证了线程的安全,但是看场景使用,并不是适用所有场景,他的优点缺点都很明显。
拓展:乐观锁,并发容器,原子类等用的是CAS来实现的吗
     是的。

14.开发过程中ABA是怎么解决的。

加标志位,例如搞个自增的字段,操作一次就自增加一,或者搞个时间戳,比较时间戳
的值。

15.简单说一下你了解的悲观锁。

synchronized 是最常用的线程同步手段之一,上面提到的CAS是乐观锁的实现,
synchronized就是悲观锁了。

16.synchronized是如何保证同一时刻只有一个线程可以进入临界区的呢。

1.synchronized对方法加锁,相当于不管哪一个线程(例如线程A),运行到这
个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他
同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这
个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。
2.synchronized 对对象进行加锁,在 JVM 中,对象在内存中分为三块区域:对象
  头(Header)、实例数据(InstanceData)和对齐填充(Padding)。
  1.对象头:我们以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:
    Mark Word(标记字段)、Klass Pointer(类型指针)。
    1.Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。它会根
      据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的
      数据会随着锁标志位的变化而变化。
    2.Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这
      个对象是哪个类的实例。
   对象头中保存了锁标志位和指向 monitor 对象的起始地址,当 Monitor 被某个
   线程持有后,就会处于锁定状态

17.在对象级使用锁通常是一种比较粗糙的方法,为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源。

1.如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将
所有线程都锁在外面。

由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:
 class FineGrainLock{
   
   MyMemberClassx,y;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值