每日三道面试题,通往自由的道路7——多线程篇
茫茫人海千千万万,感谢这一秒你看到这里。希望我的面试题系列能对你的有所帮助!共勉!
愿你在未来的日子,保持热爱,奔赴山海!
每日三道面试题,成就更好自我
今天我们继续聊一点别的吧,今天我们聊下多线程
1. 你可以讲下进程与线程的区别?为什么要用多线程?
-
进程:进程是程序的一次执行过程,是系统运行程序的基本单位。
-
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
-
区别:
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 一个线程只能属于一个进程,但是一个进程可以拥有多个线程。多线程处理就是允许一个进程中在同一时刻执行多个任务即多个线程。
- 每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
-
为什么要用多线程:
- 发挥多核CPU的优势,采用多线程的方式去同时完成几件事情而不互相干扰。
- 能够有效的防止阻塞,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。
- 提高程序的效率。
不错不错,线程知识也有涉及!
2. 什么是上下文切换?
上下文切换一般发生在多线程情况下,因为一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。而在多核cpu下,多线程是并行工作的,如果线程数多,单个核又会并发的调度线程,运行时就会让一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于上下文切换。
对于我们Java程序线程来说,一旦一个线程抢占到CPU资源的使用权后,另一个线程需要保存当前的一个状态,以便下次抢占成功后可以回到当前状态,JVM中有块内存地址叫程序计数器,用于记录保存线程执行到哪一行代码,它是每个线程独有的。执行任务从保存到再次加载的过程就是上下文切换。
实际上,上下文切换也是对系统意味着来说会消耗大量的CPU时间,消耗大量资源。
以下几种情况会发生上下文切换。
- 线程的cpu时间片用完
- 在发生垃圾回收的时候
- 我们自己调用了 sleep、yield、wait、join、synchronized、lock 等方法
可以,那问你最后一道:
3. 说说你知道的几种创建线程的方式
创建线程有以下方式:
-
继承Thread类,重载它的run方法。
- 在我们自己定义一个继承于Thread类的子类,并重写里面run方法,编写相关逻辑代码。
- 在测试类中创建我刚自定义的线程子类对象
- 调用子类实例的star方法来启动线程,通过start方法去调用到run方法里面的逻辑。
-
实现 Runnalbe接口,重载 Runnalbe接口中的run方法实现 。
- 我们定义一个实现Runnable接口实现类,并重写里面的run方法
- 在测试类中创建一个我们刚定义的接口实现类的实例,以实例对象作为target创建Thead对象,而得到的Thread对象就是我们线程子类对象。
- 最后调用线程对象的start方法
-
实现Callable接口方式,重写Callable接口中的call方法,并且这个call方法可以有返回值。
- 我们定义一个实现创建实现Callable接口实现类,并重写里面的call方法,注意它是call方法,并且有返回值。
- 在测试类中创建一个我们刚定义的接口实现类的实例,以实例对象为参数创建FutureTask对象,并把创建出来FutureTask对象作为参数去创建Thread对象,而得到的Thread对象就是我们线程子类对象。
- 最好调用线程对象的start方法。
需要注意三者的区别:
- Thread是继承,而Runnalbe、Callable是实现。对于继承来说,只能单继承,而接口可以多实现。如果继承了 Thread类就无法再继承其他类了。
- 三者都是最后采用Thread.start()去启动线程,而不是调用run方法,或者call方法的。
- Runnable接口 run 方法无返回值;Callable接口 call 方法有返回值。
- Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息
- 使用实现 Runnable接口的方式创建的线程可以处理同一资源,而实现资源的共享,还可以继承其他类。
小伙子不错嘛!今天就到这里,期待你明天的到来,希望能让我继续保持惊喜!
注: 如果文章有任何错误和建议,请各位大佬尽情留言!如果这篇文章对你也有所帮助,希望可爱亲切的您给个三连关注下,非常感谢啦!