以下这些面试题是我对着多线程的知识点自己一点点总结下来的,如果有不全的地方欢迎大家留言一起探讨。
多线程:是指这个程序运行时产生了不止一个线程
并行:多个CPU同时处理一段逻辑
并发:通过cpu调度算法,让用户觉得是在同时进行,但cpu内部并不是真正的同时
多线程是实现并发机制的一种有效手段
1、新建:在new Thread时,jvm会像普通对象一样给线程分配内存,并初始化其成员变量的值
2、就绪:当执行了start方法后,该线程便处于就绪状态,但是它并没有运行,只是表示可以运行了。(注意,当线程被阻塞后重新恢复后,必须先经过就绪状态)
3、运行:如果就绪状态的线程获得了CPU,那么执行run方法后,该线程会处于运行状态,
如果计算机只有一个CPU,那么在任何时刻,只有一个线程处于运行状态。在多处理器的机器上,会有多个线程并行执行。当线程数大于CPU数时,依然会有多个线程在同一CPU上轮换(即并发)的现象。
4、阻塞:可以理解为暂停执行该线程
5、死亡:run或call执行完成后;抛出未捕获的异常后;直接调用stop方法后,线程处于死亡状态
有哪些方法可以让线程进入阻塞状态,然后又如何恢复线程到运行状态
使线程进入阻塞状态的方法:
1、调用sleep方法主动放弃所占用的处理器资源
2、调用了一个阻塞式的IO方法:如等待某个输入输出流的完成
3、线程试图得到一个锁,而该锁正在被其他线程调用
4、线程在等待某个通知(notify)
5、调用suspend方法暂停了线程,暂停后的线程必须通过resume方法来恢复,容易造成死锁,一般不用
恢复线程到运行状态:
1、sleep方法的线程经过了指定的时间
2、阻塞式的IO方法已经返回
3、成功的获取到了试图得到的锁
4、线程正在等等某个通知时,其他线程发出了一个通知
5、调用了resume方法
可以调用线程对象的isAlive方法,当处于就绪、运行、阻塞时,返回true。当处于新建或死亡时,返回false
不能,死亡就是死亡,该线程将不可再次作为线程执行
线程从就绪状态到运行状态不受程序控制,而是根据系统线程调用所决定,当处于就绪状态的线程获得到处理器资源时,会进入运行状态。而当处于运行状态的线程失去处理器资源时,会处于就绪状态。但是yield方法可以使线程从运行状态转入就绪状态
两个线程,一个是主线程,一个是垃圾回收机制的线程
如果没有启动start而是直接启动线程的run方法会怎么样:
虚拟机会把这个线程当做一个普通的对象,而run方法也会被看做是一个普通的对象中的方法
让一个线程等待另一个线程完成,比如:我创建了一个MyThread线程:
MyThread myThread = new MyThread();
然后在main()方法中调用(本质是在main线程中调用)myThread.join()方法,那么main线程会进入阻塞状态直到MyThread线程中的run方法的执行完毕后才会继续执行。
有一种线程,是在后台运行的,它的任务是为其他线程提供服务,又称为“守护线程”,
比如JVM的垃圾回收线程就是一个后台线程。后台线程有一个特征,就是如果所有前台线程全部死亡,那么后台线程会自动死亡。
可以让线程暂停一段时间,当线程进入睡眠状态后,该线程不会获得执行机会,即使系统中没有其他可以执行的线程,处于sleep中的线程也不会执行,直到睡眠结束
最大的区别是,sleep()在睡眠后不会释放掉锁,而wait()在睡眠后会释放掉锁。
将当前正在执行的线程暂停,但是是将线程进入到就绪状态,释放CPU资源
每个线程都有一定的优先级,优先级高的线程会获得到更多的执行机会。每个线程的优先级和创建它的父线程的优先级相同,Thread提供了setPriority方法来设定优先级,参数范围为1-10
什么是线程安全,什么是线程不安全(synchronized的作用或者由来)
线程的run方法不具有同步安全性,当多个线程同时处理一个共享数据时,可能导致数据混乱,这就是线程不安全。所以引入了synchronized来解决此问题。被synchronized锁定的同步代码块,只能同时被一个线程获取到,当该线程执行完此方法后,会释放掉该锁,这时其他线程才可以继续获取该锁。Synchronized()括号内可以传任何参数,即Obj类型,但通常是被共同访问的共享资源来作为同步监视器
被synchronized修饰的方法,成为同步方法,如:
Public synchronized void getPerson(){};
当内部类中的代码块被synchronized(this)修饰时,请问这个this指的是内部类还是其父类:
内部类
当一个线程访问某个类中的被synchronized修饰的方法时,另一个线程可以访问该类中其他没有被synchronized修饰的方法吗:
可以
当一个线程访问某个类中的被synchronized修饰的方法时,另一个线程可以访问该类中其他被synchronized修饰的方法吗:
不可以,因为Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。
1、当前线程的同步方法,同步代码块执行结束
2、出现了未处理的Error或Exception,导致代码块结束
3、当线程正在执行加锁的代码块或同步方法时,调用了wait()方法,那么会使线程进入阻塞状态,并且释放锁
4、注意:当线程正在执行加锁的代码块或同步方法时,调用sleep()方法或者yield()方法,不会释放锁
当所有线程处于阻塞状态,整个程序没有发生异常,也不会有任何提示,就进入了死锁状态;
当两个线程相互等待对方释放锁时,就会发生死锁。
1、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3、Lock可以通过tryLock()方法知道是否成功获取到锁,但是synchronized不行
4、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
5、Lock可以提高多个线程进行读操作的效率(读写锁)。
6、Lock可以实现公平锁,Synchronized不保证公平性。
wait()/notify()机制:
ThreadA处于阻塞状态(wait方法),ThreadB在run方法中对某个数据做操作,当数据达到我设定的某个条件时,通过notify()来唤醒ThreadA。
好处:提高了CPU的利用率
缺点:如果通知过早,会打乱执行的逻
其他通信方法请参考:
http://www.importnew.com/26850.html
为了避免重复创建线程,线程池可以的出现可以让线程进行复用,当需要时,从线程池中取出一个线程,当工作完成后,并不是直接关闭线程,而是将线程归还给线程池供其他任务使用。
newFixedThreadPool:
固定大小的线程池,可以指定线程池的大小,该线程池corePoolSize和maximumPoolSize相等,阻塞队列使用的是LinkedBlockingQueue,大小为整数最大值。
newSingleThreadExecutor:
单个线程线程池,只有一个线程的线程池,阻塞队列使用的是LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,待空闲时再去执行。按照先入先出的顺序执行任务。
newCachedThreadPool:
缓存线程池,缓存的线程默认存活60秒。线程的核心池corePoolSize大小为0,核心池最大为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue。是一个直接提交的阻塞队列,他总会迫使线程池增加新的线程去执行新的任务。在没有任务执行时,当线程的空闲时间超过keepAliveTime(60秒),则工作线程将会终止被回收,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销。如果同时又大量任务被提交,而且任务执行的时间不是特别快,那么线程池便会新增出等量的线程池处理任务,这很可能会很快耗尽系统的资源。
newScheduledThreadPool:
定时线程池,该线程池可用于周期性地去执行任务,通常用于周期性的同步数据。
网友评论