目录
2.在学习线程章节之前,我们的程序中是否出现过“线程”的执行?
12.interrupt()方法的作用,请说说interrupt()、interrupted()和isInterrupt()的区别
13.写出可能导致线程阻塞的原因,说说哪些阻塞可以使用interrupt()中断?
18.简述synchronized锁对象的选择应遵循什么样的原则
23.请简述IllegalMonitorStateException出现的原因
26.简述yield()方法的作用,调用yield()的线程转换到什么状态?
28.什么是Daemon Thread?如何将一个线程设置为Daemon Thread?
0.请解释什么是程序、进程、线程?
程序:计算机中保存一组按照某种编程语言编写的有序的指令的集合
进程:操作系统中进行资源调度和分配的最小单元
线程:按照某一顺序执行的任务流程
1.什么是多线程?
在一个进程中,不止一条线程被执行
2.在学习线程章节之前,我们的程序中是否出现过“线程”的执行?
有JVM就是一个进行,它在运行的时候,会开启两条线程
main线程:代码中主要的执行流程
GC:守护线程,用于清理JVM堆中的垃圾对象
3.解释什么是并发,什么是并行?
并发:在同一时刻同时执行(真正意义上的同时)
并行:在同一时间段同时执行
4.创建一条线程有几种方法?各自有什么优缺点?
继承Thread类
优点:书写比较简单
缺点:子类不能在继承别的类
实现Runnable接口
优点:可以在继承别的类
缺点:书写比较麻烦,需要创建两个对象才能开启一条线程
5.线程对象调用start()和run()方法有什么区别?
调用start()方法和run()方法,都会令run()方法中的代码执行一遍
一条线程调用run()方法,会先执行run()方法中的代码,执行完成之后,线程才会继续执行
而调用start()方法,会开启一条新线程单独执行,无需等待run()方法执行完毕,start()就会快速返回,调用它的线程可以继续去执行下面的代码
6.如何获取当前正在执行的线程?
Thread.currentThread()静态方法
7.如何为线程设置名称以及如何获取线程的名称?
设置名称:
Thread的构造函数中传入name
调用Thread中的setName()方法
获取名称:
getName()
8.请简述线程的默认名称是什么
主线程:mian
额外创建的线程:Thread-0,Thread-1,Thread-2....
9.请简述线程生命周期的五大状态
新建状态:线程对象刚创建出来,没有调用start()方法
就绪状态:调用start()方法,进入就绪状态
运行状态:进入运行池当中,拿到时间片,线程被调度
堵塞状态:由于某种原因,线程不得不暂时停止运行,不能被调度
死亡状态:run()方法执行结束,全部代码都执行完毕,或者抛出异常
10.线程是否可以重复启动?
不能
一个线程对象只能调用一次start()方法。 Thread类中维护了一个变量threadStatus用来记录线程的状态。 新建状态该变量值为0。当调用start()方法之后,start()中会调用一个native修饰的本地方法start0()。该方法是开启线程的真正实现过程。该方法中会修改当前线程的状态标志位threadStatus为一个随机的正整数。 当调用start()方法的时候,会判断该标志位,如果不为0,则抛出: IllegalThreadStateException - 不合法的线程状态异常
11.请自行画出五大状态示意图,并写出五大状态的转换关系
新建:只能进入就绪,通过start()
就绪:只能进入运行,被OS调度拿到时间片
运行:
可以进入就绪,时间片用尽或主动让步yield()
可以进入阻塞,sleep()、join()、wait()、synchronized()
可以进入死亡,在某个时间片内完成了run()返回
阻塞:
可以进入就绪,timeout、join()等待的线程结束、interrupt() 拿到锁
可以进入另一种阻塞,wait()阻塞结束,进入锁池抢锁
死亡:死亡不能进入其他状态
12.interrupt()方法的作用,请说说interrupt()、interrupted()和isInterrupt()的区别
interrupt()方法用来结束中断,但是要注意的是,他不是直接结束中断的,而是设置了一个结束中断的标志位,该方法用来改变这个标志位
interrupted()和isInterrupt()用来判断这个标志位
interrupted()是一个静态方法,他用来判断正在执行线程是否被中断
isInterrupted()是一个实例方法,它用来判断调用方法的线程是否被中断
13.写出可能导致线程阻塞的原因,说说哪些阻塞可以使用interrupt()中断?
sleep()、join()、wait()可以被interrupt()中断。
由于访问同步代码块未抢到锁导致的阻塞无法被interrupt()中断。
14.下列代码是否是让t1和t2线程睡眠?解释为什么。
t1.sleep(1000);
t2.sleep(1000);
不是
sleep()是一个静态方法,跟调用的对象无关
它是让当前正在执行的线程睡眠
15.请解释join()方法的作用
join()方法用来实现线程通信,它的作用就是让线程合并,让多线程,变成单线程
比如说在A线程中调用b.join(),就是让B线程在A线程前执行
16.谈一谈线程并发会带来什么问题,以及如何解决
线程并发会带来数据不安全的问题,线程调度它是随机的,当多条线程调用一个方法或者是一个变量的时候,操作的先后顺序的不到保证,这样的话,就可能带来一些异常情况,或者计算出的数据不是我们预计的结果
主要原因是线程在某些时刻读到的数据是“脏数据”
解决的方法:线程同步,使用synchronized代码块
17.简述synchronized关键字的使用方法
使用synchronized来修饰一个代码块
当修饰一个代码块时,()里边应该声明一个用来给代码块上锁的对象
当线程访问这把锁的时候,需要先去拿到这把同步锁,如果拿到才能持有这把锁去执行代码块里边的代码
其它线程无法拿到这把锁,只能在代码块外部处于堵塞状态
使用synchronized来修饰一个方法
synchronized来修饰一整个方法时,是给整个方法上锁
如果是静态方法,则上锁的对象是,当前类的calss对象
如果是实例方法,上锁的对象就是this
18.简述synchronized锁对象的选择应遵循什么样的原则
范围不要过小
如果锁的范围过小,可能无法满足最基本的同步需求
范围不要过大
锁范围如果过大,不会太影响功能的实现,但是会引发一些不必要的争夺。 增加了线程之间争抢同步锁的激烈程度,会导致系统运行的效率降低
19.简述什么是死锁,以及如何避免死锁
在线程同步的情况下,死锁指的是两条或两条以上的线程分别占有对方的资源,在没有外力推动的情况下,互相占有对方的资源不肯首先放手,一直处于僵持状态
避免死锁:
尽量避免锁的嵌套
如果无法避免,尽量不要设置相反的取锁顺序
锁的大小要设置合理
20.请简述单例模式的六种实现方法
懒汉式
饿汉式
线程安全的懒汉式
Double-Check双重检验上锁机制
枚举
静态内部类实现
21.什么是线程通信,为什么使用线程通信
在线程并发执行的环境中,线程之间按照一定的方式进行通信
线程通信他可以改变线程的运行状态,微调局部范围内线程的执行顺序
22.简述wait()方法的作用以及使用时的注意事项
wait()方法用于拿到锁在代码块执行的线程,释放锁进入堵塞状态
wait()方法来自Object类,任何对象都可以调用wait()方法,但这个对象必须得是在synchronized修饰的代码块中充当同步锁的对象
注意事项:
1)wait()必须用在线程同步环境中
2)调用wait()方法的过程必须定义在一个synchronized()代码块中
3)wait()方法会抛出编译时异常InterruptedException
4)调用wait()之前一定要想好线程进入阻塞之后如何退出。
要么设置最大等待时间,要么在其他位置唤醒它、中断它
23.请简述IllegalMonitorStateException出现的原因
不合法的对象监听器状态异常
所谓的对象监听器其实就是同步锁
出现原因可能是没有充当同步锁的对象调用了wait(),notify(),notifyAll()等
24.notify()和notifyAll()的区别
notify()和notifyAll()会唤醒在当前对象同步锁上等待的线程
当前线程处于堵塞状态是由于当前线程调用了wait()方法而引起的
区别是:notify()会随机唤醒一条,而notifyAll()会唤醒所有的
25.什么是线程优先级,如何设置线程优先级
线程优先级用int数表示分1-10,线程默认优先级为5,数越大,表示线程的有现金越高,被调度的概率越大
不是说优先级越高,越会被调度
在双核cpu中,效果不是特别明显
在单核cpu中,效果比较显著
设置线程优先级:调用setPriority()方法
26.简述yield()方法的作用,调用yield()的线程转换到什么状态?
yield()方法是一个静态方法,作用于让当前正在运行的线程交出时间片,进入就绪状态,重新等待调度
27.yield()是否一定能将CPU转让给其他线程?
某个线程释放时间片后,下一次调度它仍然有权参加
yield()让步它不会让步给比它优先级低的线程
28.什么是Daemon Thread?如何将一个线程设置为Daemon Thread?
守护线程,精灵线程
当所有的前台线程全部结束后,当前进程中所有的守护线程都会自动死亡
gc就是一条很典型的守护线程
将一个线程设置为Daemon Thread:t.setDaemom(true);
注意:新建状态才可以设置