Java最常见的208道面试题-第三模块-Java 多线程

35.并行和并发有什么区别
答案:并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。

36.线程和进程的区别
答案:一个进程由多个线程组成,一个程序至少有一进程,一个进程至少有一个线程,进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源减少切换次数,从而效率更高.

37.守护线程是什么
答案:守护线程是服务于其他线程的线程,例如jvm的垃圾回收器

38.创建线程有哪几种方式
答案: 继承Thread类

通过Runnable 接口创建 ,

通过Callable和Future创建
创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

39.runnable和callable的区别
答案:runnable的run方法返回值是void 她只是去执行run中的代码
而callable中的方法是有返回值的是一个泛型,可以用来获取异步执行的结果

40.线程有那些状态
答案:创建,就系,运行,阻塞,死亡


41.sleep和wait有什么区别
答案:sleep是线程方法类thread中的静态方法 ,让调用线程进入睡眠,等睡眠时间结束后,线程继续进行因为sleep是静态方法,她不能改变对象锁的机制,当一个synchronized块中调用了sleep方法,线程虽然休眠但是不释放锁其他线程不萌访问这个对象.
wait是object类的方法,当一个线程执行wait方法时,它就进入到一个和该对象相关的等待池,同事释放对象锁,使得其他对象能访问,可以通过notify和notifyall唤醒等待线程

42.notify 和notifyall的区别
答案:如果线程调用了wait方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁.
当有线程调用了对象的notifyall方法(唤醒所有线程) 或notify方法(只随机唤醒一个wait线程) 被唤醒的线程便会进入该对象的锁池中.

43.现成的run和start有什么区别
答案:每个线程都是通过某个特定的Thread对象所对应的方法run来完成其操作的,方法run称为线程体,通过调用start来启动一个线程.start方法来启动一个线程,真正实现了多线程的运行,这时无序等待run方法体代码运行完毕,可以直接继续执行下面的代码.此时线程是处于就绪状态,并没有运行,然后通过thread类调用方法run来完成其运行状态,这里方法run称为线程体,包含了要执行的线程内容,run方法执行结束,线程终止,然后cpu在调度其他线程.

44.创建线程池有几种方式.
答案:newFixedThreadPool 一个固定长度的线程池,每当提交一个任务就创建一个线程,知道达到线程池的最大数量,这时线程规模不在变化,当线程发生错误关闭时,线程池会补充一个新的线程.
newCachedThreadPool
创建一个可缓存的线程池,吐过线程池的规模超过了处理需求,将自动回收空闲的线程,当需求增加时,自动创建新的线程,线程池规模不存在任何限制
newSingleThreadExecutor
创建一个单线程的,它创建当个工作线程来执行任务,如果这个线程一场结束,会创建一个新的来代替他,他的特点是能确保依照任务在队列中的顺序来串行执行.
newScheduledThreadPool
创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务.

45.线程池都有那些状态
答案:RUNNING运行状态,可以接收新任务,
SHUTDOWN关闭状态,不接收新的任务,能处理已经添加了的任务
STOP停止状态,不接收新任务,终端正在处理的任务
TIDYING
TERMINATED线程池彻底终止

46.线程池中submit和execute有什么区别
答案:接收的参数不一样,submit有返回值而execute没有,submit方便exception处理

47.在java 中怎么保证多线程的运行安全
答案:线程安全在三个方面体现.
原子性,提供互斥访问,同一时刻只能有一个线程对数据进行操作
可见性,一个线程对主内存的修改可以及时的被其他线程看到
有序性,一个线程观察其他线程中的指令执行顺序,由于指令重拍,该观察结果一般杂乱无序

48.多线程锁的升级原理是什么
答案:在java中锁有4中状态,级别从低到高依次为,无状态锁,偏向锁,轻量级锁和重量级锁,这几个状态会随着竞争情况逐渐升级锁可以升级但是不能降级

先说说为什么会有锁升级
因为Sycronized是重量级锁(也是悲观锁),每次在要进行锁的请求的时候,如果当前资源被其他线程占有要将当前的线程阻塞加入到阻塞队列,然后清空当前线程的缓存,等到锁释放的时候再通过notify或者notifyAll唤醒当前的线程,并让其处于就绪状态。这样线程的来回切换是非常消耗系统资源的,而且有的时候,线程刚挂起资源就释放了。而Java的线程是映射到操作系统的原生线程之上的每次线程的阻塞或者唤醒都要经过用户态到核心态或者核心态到用户态的转化,这样是十分浪费资源的,这样就会造成性能上的降低,因此JVM对Sychronized进行了优化,将Sycronized分为三种锁的级别:偏向锁,轻量级锁,重量级锁。
很多的时候,对于一个可能发生并发访问的对象而言,其实很少会被竞争,就算有些资源存在竞争也是在很少的一段时间资源就会被释放,而这样的情况下将线程挂起是十分浪费性能的。
偏向锁(乐观锁):
当锁对象第一次被线程获取的时候,虚拟机会将锁对象的对象头中的锁标志位设置成为01,并将偏向锁标志设置为1,线程通过CAS的方式将自己的ID值放置到对象头中(因为在这个过程中有可能会有其他线程来竞争锁,所以要通过CAS的方式,一旦有竞争就会升级为轻量级锁了),如果成功线程就获得了该轻量级锁。这样每次再进入该锁对象的时候不用进行任何的同步操作,直接比较当前锁对象的对象头是不是该线程的ID,如果是就可以直接进入。

偏向锁升级为轻量级锁
偏向锁是一种无竞争锁,一旦出现了竞争大多数情况下就会升级为轻量级锁。现在我们假设有线程1持有偏向锁,线程2来竞争偏向锁会经历以下几个过程:
1. 首先线程2会先检查偏向锁标记,如果是1,说明当前是偏向锁,那么JVM会找到线程1,看线程1是否还存活着2
2. 如果线程1已经执行完毕,就是说线程1不存在了(线程1自己是不会去释放偏向锁的),那么先将偏向锁置为0,对象头设置成为无锁的状态,用CAS的方式尝试将线程2的ID放入对象头中,不进行锁升级,还是偏向锁
3. 如果线程1还活着,先暂停线程1,将锁标志位变成00(轻量级锁)然后在线程1的栈帧中开辟出一块空间(Display Mark Word)将对象头的Mark Word置换到线程一的栈帧当中,而对象头中此时存储的是指向当前线程栈帧的指针。此时就变成了轻量级锁。继续执行线程1,然后线程2采用CAS的方式尝试获取锁。

轻量级锁与偏向锁最大的不同之处
轻量级锁和偏向锁的不同之处就在于轻量级锁对于获取锁对象采用CAS的同步方式而偏向锁直接是把整个同步过程给取消。

轻量级锁(乐观锁)
轻量级锁如何创建在上面已经讲过了,接下来说说轻量级锁如何获取锁对象,轻量级锁是通过CAS也就是自旋的方式尝试获取锁对象,一旦失败会先检查,对象头中存储的是否是指向当前线程栈帧的指针,如果是,就可以获取对象,如果不是说明存在竞争那么就要膨胀为重量级锁。轻量级锁的解锁也是通过CAS的方式尝试将对象头的Mark Word和线程中的Display Mark Word替换回来,如果成功,就释放锁,如果失败说明还有许多其他等待锁的线程(说明此时已经不是轻量级锁而是重量级锁了),会将这些线程唤醒,然后释放锁。

轻量级锁膨胀为重量级锁
一旦有两条以上的线程竞争锁,轻量级锁膨胀为重量级锁,锁的状态变成10,此时对象头中存储的就是指向重量级锁的栈帧的指针。而且其他等待锁的线程要进入阻塞状态,等待重量级锁释放再来被唤醒然后去竞争。

49.什么是死锁
答案:死锁是指两个或两个以上的进程,执行过程中,由于竞争资源或者彼此通讯而造成的一种阻塞现象

50.怎么防止死锁
答案:死锁的四个必要条件
1互斥条件,进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源.
2请求和保持条件,进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源不释放
3不可剥夺条件,进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完成后自己释放
4环路等待条件,进程发生死锁后,若干进程之间行成一种头尾详解的循环等待资源关系.
四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而是要上述条件之一不满足就不会发生死锁.

51.ThreadLocal 是什么,有哪些使用场景
答案:线程几部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享,java提供threadlocal类支持线程既不变量是一种实现线程安全的方式.

52.synchronized底层实现原理
答案: synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入高临界区,同时还可以保证共享变量的内存可见性
java中每一个对象都可以作为锁,这时synchronized实现同步的基础.
普通同步方法,锁的是挡圈实例对象.
静态同步方法,锁的是当前类的class对象.
同步方法块,锁的是括号里面的对象.

53.synchronized和volatile的区别是什么
答案:volatile的本质是在告诉jvm当前变量在寄存器中的值是不确定的需要从主内存中读取,synchronized则是锁定挡圈的变量,只有当前线程可以访问该变量,其他线程被阻塞住.
volatile仅能使用在变量级别,synchronized可以使用在变量方法和类
volatile仅能实现变量的修改可见性,不能保证原子性,而synchronized可以保证原子性和可见性
volatile不会造成线程的阻塞,synchronized会造成线程的阻塞
volatile标记的变量不会被编译器优化,synchronized标记的变量何以被编译器优化

54.synchronized和locl
答案:首先synchronized是java内置关键字,在jvm层面Lock是个java类
synchronized无法判断是否获取锁的状态,lock可以获取到锁.
synchronized会自动释放锁,a线程执行完同步代码会释放锁,b线程执行过程中发生一场会释放锁,lock需要在finally中手工释放锁,否则容易造成死锁
用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2等待,如果线程1阻塞,线程2会一直等待下去,而lock锁就不一样,如果尝试获取不到锁,线程可以不用一直等待就结束
synchronized的锁可重入,不可中断,非公平,而lock锁可重入,可判断,可公平
lock锁适合大量同步的代码的同步问题,synchronized锁适合代码量少的同步问题.

55.synchronized和ReentrantLock区别是什么
答案:synchronized是关键字,ReentrantLock是类,这时二者的本质区别.既然ReentrantLock是类,那么它就提供了比synchronized更多灵活的特性,可以被继承,可以有方法,可以有各种各样的类变量
ReentrantLock可以多获取锁的等待时间进行设置,避免死锁,可以获取各种锁的信息,可以灵活的实现多路通知
另外二者的锁的机制其实也不一样,ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的是对象头中的mark work

56.说一下atomic的原理
答案:Atomic包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。
Atomic系列的类中的核心方法都会调用unsafe类中的几个本地方法。我们需要先知道一个东西就是Unsafe类,全名为:sun.misc.Unsafe,这个类包含了大量的对C代码的操作,包括很多直接内存分配以及原子操作的调用,而它之所以标记为非安全的,是告诉你这个里面大量的方法调用都会存在安全隐患,需要小心使用,否则会导致严重的后果,例如在通过unsafe分配内存的时候,如果自己指定某些区域可能会导致一些类似C++一样的指针越界到其他进程的问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值