Java基础面试篇——多线程

什么是多线程?

首先理解什么是线程,线程是程序的执行路径,是程序的控制单元。一个进程会有一个或多个线程,当一个进程有多个执行路径,该执行方式就是多线程。

创建线程的方法?

  1. 继承Thread类,重写run()方法。

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

  1. 实现Callable接口,重写call()方法。

线程的生命周期

  • 创建(new)

  • 就绪(Runnable):线程准备运行,等待cpu调度。

  • 运行(Running):线程的代码块正在执行。

  • 阻塞(Blocked)

  • 死亡(Dead)

同步和异步

同步:线程A需要请求某资源,但是此资源正在被线程B使用,此时线程A需等待线程B执行完释放资源后再执行,这就是同步。

异步:异步与同步相对,线程B可以不用等待线程A释放资源仍可以执行。

在多线程的环境中,经常会碰到数据的共享问题,即当多个线程需要访问同一个资源时,它们需要以某种顺序来确保该资源在某一时刻只能被一 个线程使用,否则,程序的运行结果将会是不可预料的,在这种情况下就必须对数据进行同步

线程同步的实现方式

  1. 同步方法

即有 synchronized 关键字修饰的方法。

  1. 同步代码块

即有 synchronized 关键字修饰的语句块。

同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用 synchronized 代码块同步关键代码即可

  1. 使用特殊域变量(volatile)实现线程同步

  1. 使用重入锁实现线程同步

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

死锁:多个线程因抢占资源造成线程的无限等待的问题。

如何避免:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。

线程方法

  • sleep()方法:使当前线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,它没有释放锁

  • wait()方法:使当前线程进入阻塞状态,此方法必须先获取锁,即必须在同步方法或同步代码块中使用,它会释放锁,一旦一个对象调用了 wait 方法,必须要采用 notify()和 notifyAll()方法唤醒该进程

  • join()方法:线程强制执行,t1和t2两个线程,如果在t1线程里面调用了t2.join(),则t1线程会进行等待状态,t2运行结束以后,才会继续运行t1。

  • yield()方法:线程礼让,当前正在执行线程停下来一下,把执行机会让给别的在等待的线程,自己回到等待的就绪队列里面

什么是守护线程?

守护线程是个服务线程,准确的来说是服务其他的线程。Java中线程分为两种

  1. 守护线程:比如垃圾回收线程,就是最典型的守护线程。

  1. 用户线程,就是应用程序里的自定义线程。

守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连 main 线程也 执行完毕,那么 jvm 就会退出(即停止运行)——此时,连 jvm 都停止运行了,守护线程当然也就停止执行了。

什么是悲观锁和乐观锁?怎么实现?

悲观锁:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁, 这样别人想拿这个数据就会阻塞直到它拿到锁。

实现方式:

  • 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁, 表锁等,读锁(共享锁),写锁(排他锁)等,都是在做操作之前先上锁。

  • Java 里面的同步原语 synchronized 关键字。


乐观锁:

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

实现方式:

  • CAS

  • 添加版本号:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会+1。当线程A要更新数据值时,在读取数据的同时也会读取version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值