JAVA面试题多线程&并发篇(一)
文章目录
- JAVA面试题多线程&并发篇(一)
- 前言
- 一、说说java中实现多线程有几种方法。
- 二、如何停止一个正在运行的线程
- 三、notify()和notifyAll()有什么区别?
- 四、sleep()和wait()的区别?
- 五、volatile是什么?可以保证有序吗?
- 六、Thread类中的start()和run()方法有什么区别?
- 七、为什么wait、notify、notifyall这些方法不在thread类里?
- 八、为什么wait和notify方法要在同步块中调用?
- 总结
前言
到了大三的学期的暑假,即将面临找工作的考验。希望这份面试资料能够帮住大家学到自己的知识盲区。加油,我命由我不由天!!!
一、说说java中实现多线程有几种方法。
创建线程的常用三种方式:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口
4、线程池方式创建
通过继承Thread类或者实现Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法返回值,可以声明抛出异常而已。因此将实现Runnable接口和实现Callable接口归为一种方式。这种方式与继承Thread方式之间的主要差别如下:
采用实现Runnable类、Callable接口的方式创建线程的优缺点
优点: 线程类只是实现了Runable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况。从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
缺点: 编程稍微复杂一些,如果需要访问当前线程,则必须使用Thread.currentThread()方法。
** 采用继承Thread类的方式创建线程的优缺点**
优点: 编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获取当前线程。
缺点: 因为线程类已经继承了Thread类,java语言是单继承的,所以就不能继承其它类了。
二、如何停止一个正在运行的线程
1、使用推出标志,使线程正常退出,也就是当run方法完成后线程终止。
2、使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
三、notify()和notifyAll()有什么区别?
notify会导致死锁,notifyAll则不会。
任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized中的代码使用notifyAll,可以唤醒所有处于wait的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。
wait()应该配合while循环使用,不应该使用if(),务必在wait()调用前后都检查条件,如果不满足,必须调用notify唤醒另外的线程来处理,自己继续wait直至条件满足再执行下去。
四、sleep()和wait()的区别?
对于sleep()方法,我们首先要知道方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会恢复到运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。
当wait方法的时候,线程会放弃对象锁,进入等待此对象的等待定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。
五、volatile是什么?可以保证有序吗?
一但一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了俩层语义:
1、保证了不同的线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,vilatile关键字会强制将修改的值立即写入主存。
2、禁止进行指令重排序。
volatile不是原子性操作
什么叫保证部分有序性?
当程序执行到volatile变量的读操作或写操作时,在其他的操作的更改肯定全部已经进行,且结果已经对后面的操作可见:在其后面的操作肯定还没有进行;
六、Thread类中的start()和run()方法有什么区别?
start用来启动新创建的线程,而且start内部调用了run方法,这和直接调用run方法的效果不同。当年调用run方法时,只会在原来的线程中调用,没有新的线程启动,start方法才会启动新线程。
七、为什么wait、notify、notifyall这些方法不在thread类里?
明显的原因是JAVA提供的锁是对象级的不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁那么调用对象中的wait()方法就有意义了。如果wait方法在Thread类中,线程正在等待的是哪个锁就不明显了。简单的说,由于wait,notify和notifyall都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。
八、为什么wait和notify方法要在同步块中调用?
1、只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方
法。
2、如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。
3、还有一个原因是为了避免wait和notify之间产生竞态条件。
总结
线程和并发都是非常关键的知识点。整理不易,欢迎大家一起交流,喜欢的朋友记得关注我点赞哟,感谢支持!将会持续的更新下去,你们的点赞和关注是我继续的动力!!!