多线程面试题总结
1.进程和线程的区别
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
2.Callable接口和Runnable接口的区别
(1) Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
(2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3) call方法可以抛出异常,run方法不可以。
(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
3.线程六种状态的切换
4.start()方法的调用和run()方法调用有什么区别?
1.调用start()方法:
通知“线程规划器”当前线程已经准备就绪,等待调用线程对象的run()方法。这个过程就是让系统安排一个时间来调用Thread中的run()方法,使线程得到运行,启动线程,具有异步执行的效果。
调用start()方法,也就是线程状态转变成可运行状态的过程。
2.调用run()方法:
不是异步执行,而是同步执行,当前线程并不交给“线程规划器”来处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。
5.总结sleep和yield的区别
- sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
- sleep()可使优先级低的线程得到执行的机会,当然也可以让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
6.isInterrupted和interrupted方法之间的区别
interrupted()是静态方法,它调用的是currentThread().isInterrupted(true)方法,即说明是返回当前线程的是否已经中断的状态值,而且有清理中断状态的机制。而isInterrupted()是一个实例方法,它调用的是isInterrupted(false)方法,意思是返回线程是否已经中断的状态,它没有清理中断状态的机制。
7.守护线程
当有一个无限循环的线程,当其他程序执行完后,他就需要关闭,这时应该设置他为守护线程,设置方法为
Thread t = new MyThread();
t.setDaemon(true);
t.start();
8.并发编程的三大特性
1、原子性
对于成员变量a来说,如果线程A执行以下操作:
a++;
此时需要分三步执行:
(1)读取a的值
(2)将a的值加1
(3)将加1后的值赋给a
在执行以上三步过程中,如果另一个线程B对a进行了操作,那么就不能保证原子性了。
要保证原子性,可以加锁,如synchronized
2、可见性
要理解可见性,需要先理解cpu的高速缓存。高速缓存是cpu的一块儿缓存区。如果线程修改了某个变量的值,那么是先将修改过的值先放入缓存区,然后满足一定条件后才会同步到内存区。同步到内存区后,其他线程才可以看见变量的改变。
可见性是指,当有一个线程修改某个成员变量的值时,其他变量可以立马看到修改过的值。
3、有序性
处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
9.synchronized同步代码块和同步方法的底层原理
1)每一个对象都与一个monitor相关联,一个monitor lock只能被一个线程在某一时间内获得,
在一个线程尝试获取与对象关联的monitor lock的时候会发生如下事情:
- a.如果monitor的计数器为0,表示当前monitor lock没有被任何线程锁获取,某个线程获取之后,需要对该计数器进行加一,表示当前的线程是这个monitor的owner
- b.如果一个已经拥有monitor lock的线程再次想要拥有这个monitor lock,会使得monitor的计数器累加(synchornized是一个可重入锁)
- c.如果monitor的计数器不为0,被其他线程所拥有,此时该线程尝试获取monitor lock,会被陷入阻塞,直到monitor的计数器再次为0,才能够再次尝试获取monitor的使用权
2)monitorexit释放对monitor的使用权,将monitor的计数器减为1,如果计数器为0,表示该线程不再拥有monitor的所有权
同步方法 静态常量池中多一个ACC_SYNCHORNIZED标识符,表示当前的方法是同步方法