主要整理一些关于线程的知识,尽量做到言简意赅,面试的时候用。
线程
前段时间找工作,很多次问到关于线程的问题,回答的时候就开启背书模式:线程是轻量级的进程,是程序执行的最小单位….现在想想,面试官要听的肯定不是这些,如果只知道这些,那还怎么守护达康书记的GDP。
面试官:来说下线程吧。
程序猿:线程用的是Thread这个类和Runnable这个接口…
面试官:那你来说下Thread和Runnable的区别。
程序员:一个继承Thread方法,重写run()方法。另一个实现Runnable接的run()方法,两者都是通过start()方法实现多线程。
扩展:start()表示创建一个新的线程,而run()只是在当前线程里调用run()方法,Runnable和Thread比较:
- JAVA是单继承,Thread有局限性;
- Runnable数据共享;
面试官:说下线程的几个方法。
程序员:主要用的是这么几个:
- stop():停止线程,不过线程已经废弃了,因为这个方法太暴力了,说停就停。比如,A写一个数据data,B等待去读写到一半的时候,调用了stop,数据损坏,B此时去读的话,就是错误的数据了,解决方法就是,自定义停止线程的条件。
- wait()和notify():当一个对象调用了waut后,当前线程会一直等待,直到其他线程调用了notify。notify和notifyAll的区别:nofity在等待结束后,会随机抽取一个线程,nofityAll是所有线程。wait和sleep的区别:wait可以重新唤醒,会释放目标对象的锁。大概是下图那个意思,重点是流程,现实中开明的父母还是很多的哈
- suspend()和resume():不推荐用,不会释放锁。
- join()和yield():join是等待线程完成后再去执行.。比如:
class OneThread extends Thread{
@Override
public void run() {
for(int i =0;i<1000000;i++);
}
}
直接调用OneThread.start(),输出i结果会很小,调用OneThread.join,会等待线程执行完成,输出i=1000000。
面试官:说一下volatile吧。
程序猿:volatile修饰表示不能随意改变目标指令。
这跟JAVA的内存模型有关系,JVM的三个特征:
- 原子性:不能中断,比如int a,两个线程无论用任何方式赋值,最后a的结果只能是两个中的一个。PS:long类型比较特殊,因为他有64位,在32为系统上有出入。
- 可见性:窜行程序,可见性问题不存在,修改值后,后续的操作都是修改后的值。并行程序会出现问题:A修改了一个全局变量,其他线程不一定知道。
- 有序性:指令重排,后面的代码肯呢个会先执行,无法判断会不会出现这个问题,同样在窜行中不会出现。
在实际的运用中,我们常用的单例模式,比较好的写法其实是这样:
public class Simple{
private static volatile Simple simple;
public static Simple getInstance(){
if (null == simple) {
synchronized (Simple.class) {
if (null == simple) {
simple = new Simple();
}
}
}
return simple;
}
}
这样写效率低了些,但保证了安全性。
面试官:说下synchronized的作用
程序员:实现线程间的同步,对需要同步的代码加锁,比如A在写入时,B不能读也不能写,保证线程间的安全性。主要的几种用法:
- 指定对象加锁;
- 作用于实例方法;
- 作用于静态方法;
最后会问一些其他跟线程有关的问题,比如HashMap,ArrayList是线程安全吗?为什么?这些网上都有很多,可以去找下这方面的资料