1.什么是进程,什么是线程?
进程是程序的一次执行过程,是系统执行的基本单位,运行一个程序也是从创建进程到进程消亡的过程。 线程又称轻量级进程,是比进程更小的执行单位。一个进程在执行的过程中会分为多个线程。线程间共享进程的堆和方法区,每个线程有自己的程序计数器、虚拟机栈、本地方法栈。
2.请简要描述进程与线程的关系,区别,优缺点?
一个进程可以包含多个线程,线程之间共享当前进程的堆和方法区,线程运行期间会相互影响,但各进程之间相互独立。线程执行时性能开销小,进程开销较大。
3.程序计数器为什么是私有的?
程序计数器有两个功能: 1.字节码解释器通过改变程序计数器来读取指令,从而实现对代码的控制。 2.多线程程序运行期间在线程切换时,会通过程序计数器记录切换之前指令的执行位置。从而恢复切换之前的正确运行状态。 程序记录器的存在是为了线程切换后能恢复到正确的执行位置。
4.虚拟机栈和本地方法栈为什么是私有的?
虚拟机栈:每个Java方法在执行时会创建一个栈帧存放局部变量表、操作数和常量池引用等信息,从方法的调用到执行完成,对应着一个栈帧在虚拟机中入栈和出栈的过程。 本地方法栈:和虚拟机栈作用类似。虚拟机栈时为虚拟机执行java方法而服务,本地方法栈为虚拟机使用的Native方法服务。 虚拟机栈和本地方法栈保证了线程的局部变量不被其他线程访问到。
5.堆和方法区的作用?
堆和方法区都是所有线程共享的资源。堆是进程中最大的一块内存,用来存放所有创建的对象,方法区用来存放已加载的类信息、常量、静态变量。
6.并发和并行的区别
并发:同一时间段内,多个任务都在执行(单位时间内不一定同时执行)。 并行:单位时间内,多个任务同时执行。
7.为什么要使用多线程?
线程是比进程更小的程序执行单位,拥有更小的性能开销。现在的计算机大多为多核,单线程只会占用一个cpu,其他cpu则处于闲置状态,没有得到充分的利用。再者,时代对系统的并发性提出了更高的要求,利用多线程机制可以提高系统的并发能力和性能。
8.线程的生命周期和状态
线程的五种基本状态:新建、就绪、运行、阻塞、死亡。 新建:
线程对象新建后,进入新建状态
就绪:
线程调用start()方法后,进入就绪状态,等待CPU调度。
运行:
CPU从可运行线程池中调度就绪线程,获得CPU使用权,进入运行状态
阻塞:
处于运行状态的线程因为某种原因,暂停对CPU的使用权,进入阻塞状态,直至其进入到就绪状态,等待CPU的调度执行。 1.等待阻塞 线程调用wait()方法,使线程进入等待阻塞状态,需要依靠其他线程的通知才能重新进入就绪状态。 2.同步阻塞 线程在获取synchronized同步锁失败(因锁被其他线程占用),进入到同步阻塞状态。在获取到锁后,进入就绪状态。 3.其他阻塞 线程通过调用sleep()方法、join()方法和IO请求,使得进程进入到阻塞状态,当sleep()状态超时,join()方法等待进程终止或者超时、IO请求处理完毕,线程转入就绪状态。
死亡:
线程执行完毕或异常退出,该线程结束生命周期。
线程创建方式:
1.继续Thread类并重写run方法。
public class Test extends Thread { @Override public void run() { // TODO Auto-generated method stub System.out.println("This is myThread");; } public static void main(String[] args) { Test test = new Test(); test.start(); } }
2.实现Runnable接口并重写run方法。
public class Test implements Runnable { @Override public void run() { // TODO Auto-generated method stub System.out.println("This is myThread implements Runnable"); } public static void main(String[] args) { Test test = new Test(); Thread td = new Thread(test);//td才是真正的线程对象 td.start(); } }
线程状态控制
1.线程休眠sleep() sleep()是静态方法,最好别用Thread的实例对象调用,sleep()方法睡眠的始终是当前正在运行的线程,而不是调用它的线程对象。
public class Test extends Thread { @Override public void run() { // TODO Auto-generated method stub System.out.println("childThread"); } public static void main(String[] args){ Test test = new Test(); test.start(); try { Thread.sleep(1000); System.out.println("main"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //运行结果: childThread//打印出此结果后隔1s打印main main
2.线程让步yield() 该方法使线程从运行态转为就绪态,CPU从就绪状态队列中只会选择与该线程优先级相同或者是优先级更高的线程去执行。
public class Test extends Thread { @Override public void run() { // TODO Auto-generated method stub System.out.println("childThread"); } public static void main(String[] args){ Test test = new Test(); test.start(); for(int i= 0; i < 4; i++){ Thread.yield();//主线程放弃使用cpu转为就绪状态,但cpu调度也可能继续调用主线程 System.out.println("main"); } } } //运行结果://结果不固定 main | main childThread | main main | main main | main main | childThread
3.线程合并join() 线程合并即将多个并发线程合并为一个单一线程来执行。一个线程的执行必须要等到其他线程执行完毕后才可执行。
public class Test extends Thread { @Override public void run() { // TODO Auto-generated method stub System.out.println("childThread"); } public static void main(String[] args){ Test test = new Test(); test.start(); // try { // test.join(); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } System.out.println("main"); } } //运行结果: 当有注释时打印:main childThread 无注释时打印:等待test线程执行结束再执行主线程 childThread main
sleep()和yield()的区别: 1.sleep()方法使线程进入阻塞状态,等睡眠时间结束,线程进入就绪状态;yield()方法使正在执行的线程进入就绪状态,有可能刚进入就绪状态,又被调度到CPU状态。 2.sleep()方法需抛出InterruptedException,需要捕获或者抛出。而yield()方法不需要。 3.sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法控制并发线程的执行。
9.sleep() 方法和 wait() 方法区别和共同点?
1.两者都可以暂停线程运行。 2.wait()通常用于线程间交互/通信,sleep()通常用于暂停执行。 3.调用wait()方法的进程需要其他进程调用同一个对象上的notify()方法才能苏醒,而sleep()在睡眠时间超时后自动苏醒。
10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
调用start()方法会使线程进入就绪状态,经cpu调度后会自动执行run()方法,而直接调用run()方法会把它当作一个普通方法,在main()线程中执行,不是真正的多线程。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/290637.html