注:资料有书为证,大伙可放心翻阅并熟记,由本人亲自总结!
1、什么是线程?它和进程有什么关系?为什么要使用多线程?
线程是指再程序再执行过程中,能够执行程序代码的一个执行单元。它有四种状态:运行、就绪、挂起和结束。
线程也被称作轻量级进程,一个进程可以含有多个线程,各个线程之间共享程序的内存空间,但是各个线程又拥有自己的栈空间。
使用多线程能够给我们减少程序的响应时间;与进程相比,线程的创建和切换开销更小,多线程在数据共享方面效率非常高;多CPU或多核计算机本身就具有执行多线程的能力;使用多线程能简化程序的结构,是程序便于理解和维护。
2、同步和异步有什么区别?
实现同步的方式有两种:一种是利用同步代码块来实现同步;另一种是利用同步方法来实现同步。
异步和非阻塞类似,由于每个线程都包含了运行时自身所需要的数据或方法,因此,在进行输入输出处理时,不必关心其他线程的状态或行为,也不必等到输入输出处理完毕才返回。
3、如何实现Java多线程?
①、继承Thread类,重写run()方法。
②、实现Runnable接口,并实现该接口的run()方法。
1)、自定义类并实现Runnable接口,实现run()方法。
2)、创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。
3)、调用Thread的start()方法。
③、实现Callable接口,重写call()方法。
1)、Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能。
2)、Callable中的call()方法可以抛出异常,而Runnable的run()方法不能抛出异常。
3)、运行Callable可以拿到一个Future对象,Future对象标识异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于有计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用Future来监视目标线程调用call()方法的情况,当调用Future的get()方法以获取结果时,当前线程就会阻塞,直到call()方法结束返回结果。
拓展:一个类是否可以同时继承Thread与实现Runnable接口?
可以,这个时候其实对于run()方法的实现可有可无,如果你没有实现run()方法,则会默认从Thread那里继承run()方法,当然,你也可以手动去实现run()方法。
4、run()方法与start()方法有什么区别。
start()是用来开启一个线程的,而run()方法则是线程当中实际执行的操作,当run()方法结束后,线程也会终止。
如果直接调用线程类的run()方法,会被当做一个普通的函数调用,程序仍然只有主线程这一个线程,无法达到多线程的目的。
也就是说,start()方法能够异步地调用run()方法,但是直接调用run()方法却是同步的,同步就无法实现多线程了。
5、多线程同步的实现方法有哪些?
①、synchronized关键字:有synchronized方法和synchronized块。
②、wait()方法与notify()方法。
③、Lock,实现类ReentrantLock(可重入锁):lock()、tryLock()、tryLock(long timeout,TimeUnit unit)、lockInterruptibly()、unLock()。
6、sleep()方法与wait()有什么区别?
①、原理不同。sleep()方法是Thread类的静态方法,是线程用来控制自身流程的。它会使此线程暂停一段时间,而把执行机会让给其他线程,等到计时时间一到,此线程会自动苏醒。而wait()方法使Object类的方法,用于线程间的通信,这个方法会使得当前拥有该对象锁的进程等待,直到其他线程调用notify()方法时才会苏醒,不过开发人员也可以自己指定一个时间。
②、对锁的处理机制不同。sleep()方法不会释放锁;而wait()方法被调用后,线程会释放掉它锁占有的锁。
③、使用区域不同。wait()方法需要放到同步控制方法中或者同步语句块中;但sleep()方法则可以放在任何地方使用。
④、sleep()方法必须捕获异常,而wait()、notify()以及notifyAll()则不需要捕获异常。
拓展:sleep()方法与yield()方法有什么区别?
①、sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程先运行的机会,而yield()方法只会给相同优先级或更高优先级的线程以运行的机会。
②、执行了sleep()方法的线程在指定时间内不会被执行,陷入睡眠(阻塞)状态。但yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程有可能在进入到可执行状态后马上又被执行。
③、sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常。
④、sleep()方法比yield()方法具有更好的可以执行。
7、终止线程的方法有哪些?
可以使用stap()方法与suspend()方法来终止线程的执行。
但是stop()方法可能会导致程序的不确定,而使用suspend()方法如果挂起了一个有锁的线程,那么在锁恢复之前不会被释放,就可以能会导致死锁的情况。
具体如何才能终止线程呢:一般建议采用的方法是让线程自行结束进入Dead状态。一个线程进入Dead状态,即执行完run()方法,也就是说,如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行。在执行时,可以通过设置一个flag标志来控制循环是否执行,通过这种方法来让线程离开run()方法从而终止线程。
这样做虽然能够终止线程,但是同样存在问题:当线程处于非运行状态的时候(sleep()方法被调用或当wait()方法被调用或当被IO阻塞时),上面介绍的方法就不可用了。此时可以使用interrupt()方法来打破阻塞的情况,当interrupt()方法被调用时,会抛出中断异常,可以通过在run()方法中捕获这个异常来让线程安全退出。
而对于IO阻塞的化,则是捕获IOException异常!
8、synchronized与Lock有什么异同?
①、用法不同。synchronized是托管给JVM执行的,我么只要加个关键字就可以了;而Lock的锁定是通过我们手动代码实现的,它比synchronized有更精确的线程语义。
②、性能不一样。在资源竞争不是很激烈的情况下,synchronized的性能要优于ReentranLock,但是在资源竞争很激烈的情况下,synchronized的性能会下降得非常地,而ReentrantLock基本保持不变。
③、锁机制不一样。synchronized获得锁和释放得方式都是在块结构中,当获得多个锁是必须以相反得顺序释放,并且是自动解锁,不会因为除了异常而导致锁没有被释放从而发生死锁。而Lock则需要手动释放,且得再finally块中释放,否则会引起死锁。
拓展:当一个线程进入一个对象的一个synchronized()方法后,其他线程是否可以进入此对象的其他方法呢?
建议参考8锁现象!!!
9、什么是守护线程?
任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,它们是为非守护线程服务的。
只要有任何非守护线程还在运行,守护线程就不会死完,程序就不会终止。
当一个守护线程中产生了一个其他线程,那么这些新产生的线程默认还是守护线程,用户线程也是如此。
守护线程一个经典的例子就是垃圾回收器,只要JVM启动,它始终再运行,实时监控和管理系统中可以被回收的资源。
10、join()方法的作用是什么?
在Java中,join()方法的作用是让调用该方法的线程在执行完run()方法后,再执行join()方法后面的代码。简单的说,就是将两个线程合并,用于实现同步的功能。
其实也可以用来等待线程完成,实现线程依次执行的效果,具体怎么完成就要看自己操作了,反正join()可以这样来用,利用等待的时间来实现线程依次执行的效果。
转载麻烦请附录地址,感谢大家配合!!!
https://blog.youkuaiyun.com/HOLLOWYANG/article/details/119987011