赞
踩
C++会对进程有更进一步介绍,例如:如何通过编写代码,来进行进程的控制(多进程编程),但是Java并不太关注这些
JVM也不是完全没有,也提供了非常粗糙的多进程操作的api,但是控制过程不如C++通过系统原生api更精细
当前的CPU都是多核心CPU,需要通过一些特定的编程技巧,把要完成的任务,拆解成多个部分,并且分别让他们在不同的CPU核心上运行,也就是**“并发编程”**
所以引入线程,来解决上述进程“太重量”的问题
线程(thread),也称为“轻量级进程”,创建和销毁的开销更小,线程可以理解成“进程的一部分”,一个进程中可以包含一个线程或者多个线程,描述进程,使用PCB这样的结构体,事实上,更严格地说,一个PCB其实是描述一个线程的,若干个PCB联合在一起,是描述一个进程的
PCB:pid(每个线程都不一样)、内存指针、文件描述符表、状态、上下文、优先级、记账信息、tgid(同一个进程的tgid是同一个)
同一个进程的若干个线程,是共用相同的内存资源和文件资源的,这里的内存指针和文件描述符表其实是同一个,但是每个线程都是独立在CPU上调度执行
引入线程后,就可以每个客户端分配一个线程来处理,起到优化效果
为什么线程比进程更轻量/为什么说线程创建和销毁的开销比进程更小
核心在于,创建进程,可能要包含多个线程,这个过程中,涉及到资源分配/资源释放,创建线程,相当于资源已经有了,省去了资源分配/资源释放步骤了,同一个进程包含N个线程,这些线程之间是共用资源的,只有创建第一个线程(也是创建进程的时候),去进行资源申请操作,后续再创建线程,都没有申请资源的过程了
线程本身是操作系统提供的,操作系统提供了api让我们操作线程,JVM就对操作系统api进行了封装,Java中提供了Thread类,表示线程
public void run()
:run只是描述了线程要干啥任务,run不是start调用的,是start创建出来的线程,线程里被调用的
t.start();
:Thread类中自带的方法,调用操作系统提供的“创建线程”api,在内核中创建对应的PCB,并且把PCB加入到链表中,进一步的系统调度到这个线程之后,就会执行上述run方法中的逻辑
像run这种方法,只是定义好,而不用去手动调用,把这个方法的调用,交给系统/其他的库/其他的框架(别人)调用这样的方法(函数)称为**“回调函数”**(callback function)
package thread; class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public class Demo1 { public static void main(String[] args) { Thread t = new MyThread(); t.start(); for (int i = 0; i < 5; i++) { System.out.println("hello main"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
上述代码中有两个线程
JDK中包含了jconsole工具,通过这个工具可以更直观地看到内部进程的情况,里面除了main与t线程,剩下的线程,都是JVM帮我们做的一些其他工作,有的是负责垃圾回收的,有的是负责记录调试信息的
Thread.sleep(1000);
:让线程主动进入“阻塞状态”,主动放弃去CPU上执行,时间到了之后,线程才会接触阻塞状态,重新被调度到CPU上执行,加上sleep就让CPU消耗的资源大幅度降低了,不加入sleep,消耗CPU资源将会特别大,while循环太快了
未来实际开发中,如果服务器程序消耗CPU的资源超出预期,如何排查
上述代码补充说明
Runnable的作用,是描述了一个“任务”,这个任务和具体的执行机制无关(通过线程的方式执行,还是通过其他的方式执行),run就是要执行的任务内容本身了
引入Runnable就是为了解耦合,未来如果要更换其他的方式来执行这些任务,改动成本比较低,把任务内容和线程这个概念给拆分开了,这样的任务,就可以给其他的地方来执行
package thread; class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public class Demo2 { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); for (int i = 0; i < 5; i++) { System.out.println("hello main"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
package thread; public class Demo3 { public static void main(String[] args) { Thread t = new Thread() { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }; t.start(); for (int i = 0; i < 5; i++) { System.out.println("hello main"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
此处的new Thread()
package thread; public class Demo4 { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }); t.start(); for (int i = 0; i < 5; i++) { System.out.println("hello main"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
此处匿名内部类,只是针对Runnable,和Thread没有关系,只是把Runnable的实例,作为参数传入到了Thread的构造方法中
lambda本质上就是针对匿名内部类的平替
package thread; public class Demo5 { public static void main(String[] args) { Thread t = new Thread(() ->{ for (int i = 0; i < 5; i++) { System.out.println("hello thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); t.start(); for (int i = 0; i < 5; i++) { System.out.println("hello main"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。