文章目录
进程?
进程(Process)是程序的⼀次执⾏过程,是系统运⾏程序的基本单位,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。因此进程是动态的。系统运⾏⼀个程序即是⼀个进程从创建,运⾏到消亡的过程。
在 Java 中,当我们启动 main 函数时其实就是启动了⼀个 JVM 的进程,⽽ main 函数所在的线程就是这个进程中的⼀个线程,也称主线程。
如下图所示,在 windows 中通过查看任务管理器的⽅式,我们就可以清楚看到 window 当前运⾏的进程(.exe ⽂件的运⾏)。
线程?
线程与进程相似,但线程是⼀个⽐进程更⼩的执⾏单位。⼀个进程在其执⾏的过程中可以产⽣多个线程。与进程不同的是同类的多个线程共享进程的堆和⽅法区资源,但每个线程有⾃⼰的程序计数器、 虚拟机栈和本地⽅法栈,所以系统在产⽣⼀个线程,或是在各个线程之间作切换⼯作时,负担要⽐进程⼩得多,也正因为如此,线程也被称为轻量级进程。
⼀个 Java 程序的运⾏是 main 线程和多个其他线程同时运⾏。
进程与线程的关系?
推荐阅读:
进程与线程的一个简单解释:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
进程让操作系统的并发性成为可能,而线程让进程的内部并发成为可能。
进程是资源分配的最小单位,线程是执行的最小单位
进程并发:操作系统已经帮我们处理完成
线程并发:Java采用的是单线程编程模型,即在我们自己的程序中如果没有主动创建线程的话,只会创建一个线程,通常称为主线程。但是要注意,虽然只有一个线程来执行任务,不代表JVM(在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程))中只有一个线程,JVM实例在创建的时候,同时会创建很多其他的线程(比如垃圾收集器线程)。原文链接:https://blog.csdn.net/hejingyuan6/article/details/46722779
Java 内存区域,通过下图我们从 JVM 的⻆度来说⼀下线程和进程之间的关系
从上图可以看出:⼀个进程中可以有多个线程,多个线程共享进程的堆和⽅法区 (JDK1.8 之后的元空间)资源,但是每个线程有⾃⼰的程序计数器、 虚拟机栈 和 本地⽅法栈。
总结:
1.线程 是 进程 划分成的更⼩的运⾏单位。
2.线程和进程最⼤的不同在于基本上各进程是独⽴的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互影响。
3.线程执⾏开销⼩,但不利于资源的管理和保护;⽽进程正相反
程序计数器为什么是私有的?
程序计数器主要有下⾯两个作⽤:
- 字节码解释器通过改变程序计数器来依次读取指令,从⽽实现代码的流程控制,如:顺序执⾏、选择、循环、异常处理。
- 在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时候能够知道该线程上次运⾏到哪⼉了。
需要注意的是,如果执⾏的是 native ⽅法,那么程序计数器记录的是 undefined 地址,只有执⾏的是 Java 代码时程序计数器记录的才是下⼀条指令的地址。所以,程序计数器私有主要是为了线程切换后能恢复到正确的执⾏位置。
虚拟机栈和本地⽅法栈为什么是私有的?
虚拟机栈: 每个 Java ⽅法在执⾏的同时会创建⼀个栈帧⽤于存储局部变量表、操作数栈、常量池引⽤等信息。从⽅法调⽤直⾄执⾏完成的过程,就对应着⼀个栈帧在 Java 虚拟机栈中⼊栈和出栈的过程。
本地⽅法栈: 和虚拟机栈所发挥的作⽤⾮常相似,区别是: 虚拟机栈为虚拟机执⾏ Java ⽅法(也就是字节码)服务,⽽本地⽅法栈则为虚拟机使⽤到的 Native ⽅法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合⼆为⼀。
所以,为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地⽅法栈是线程私有的。
堆和⽅法区为什么是公有的?
堆和⽅法区是所有线程共享的资源,其中堆是进程中最⼤的⼀块内存,主要⽤于存放新创建的对象 (所有对象都在这⾥分配内存),⽅法区主要⽤于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
并发与并行的区别?
并发(concurrency): 同⼀时间段,多个任务都在执⾏ (单位时间内不⼀定同时执⾏);
在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。
并行(parallel): 单位时间内,多个任务同时执⾏
在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。
解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
解释三:并行是在多台处理器上同时处理多个任务。如 hadoop 分布式集群,并发是在一台处理器上“同时”处理多个任务。
为什么要使用多线程?
先从总体上来说:
从计算机底层来说: 线程可以⽐作是轻量级的进程,是程序执⾏的最⼩单位,线程间的切换和调度的成本远远⼩于进程。另外,多核 CPU 时代意味着多个线程可以同时运⾏,这减少了线程上下⽂切换的开销。
从当代互联⽹发展趋势来说: 现在的系统动不动就要求百万级甚⾄千万级的并发量,⽽多线程并发编程正是开发⾼并发系统的基础,利⽤好多线程机制可以⼤⼤提⾼系统整体的并发能⼒以及性能。
再深⼊到计算机底层来探讨:
单核时代: 在单核时代多线程主要是为了提⾼ CPU 和 IO 设备的综合利⽤率。举个例⼦:当只有⼀个线程的时候会导致 CPU 计算时, IO 设备空闲;进⾏ IO 操作时, CPU 空闲。我们可以简单地说这两者的利⽤率⽬前都是 50%左右。但是当有两个线程的时候就不⼀样了,当⼀个线程执⾏ CPU 计算时,另外⼀个线程可以进⾏ IO 操作,这样两个的利⽤率就可以在理想情况下达到100%了。
多核时代: 多核时代多线程主要是为了提⾼ CPU 利⽤率。举个例⼦:假如我们要计算⼀个复杂的任务,我们只⽤⼀个线程的话, CPU 只会⼀个 CPU 核⼼被利⽤到,⽽创建多个线程就可以让多个 CPU 核⼼被利⽤到,这样就提⾼了 CPU 的利⽤率。
多线程实现?
多线程实现之—继承Thread类
步骤
1、自定义线程类继承Thread类
2、重写run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
多线程实现之—实现Runnable接口
步骤
1、自定义线程类实现Runnable接口
2、重写run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
对比:继承Thread类vs实现Runnable接口
通过上面大家对比一下应该也发现了,两种方法只有第一步存在差别,但是,启动线程他们也是有区别的
对比:
继承Thread类:
子类继承Thread类具备多线程能力
启动线程:子类对象.start()
不建议使用:避免oop单继承局限性
实现Runnable接口:
实现接口Runnable具有多线程的能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
多线程实现之—实现Callable接口
1、实现Callable接口,需要返回值类型
2、重写call方法,需要抛出异常
3、创建目标对象
4、创建执行服务:ExecutorServices ser=Executors.newFixedThreadPool(1);
5、提交执行:Futureresult1=ser.submit(t1);
6、获取结果:boolean r1=result.get();
7、关闭服务:ser.shutdownNow();
对比:普通方法调用vs多线程
不知道大家有没有发现,我们重写run()方法,但是最后却调用的是start()方法
执行run方法,只有一个线程
执行start方法,有两个线程
执行的第一行代码是14行代码
分为两个线程,一个main主线程,一个thread线程
区别
run方法自始至终都只有一个线程就是主线程
start方法有一个主线程只执行main方法的,run方法通过另一个线程也就是thread-0执行
方法级别和线程级别的区别
线程池?
什么是?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。
为什么使用线程池?
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;
下面我们针对运行效率做个简单测试:
使用普通线程
使用线程池
很明显,使用线程池之后大大提高了效率
使用线程池有哪些优势?
1、线程和任务分离,提升线程重用性;
2、控制线程并发数量,降低服务器压力,统一管理所有线程;
3、提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
使用线程池的缺点?
1、如果不对线程进行加锁处理 ,容易造成线程死锁
2、使用线程池对程序员要求高
3、容易造成堆栈溢出
总结
啊哦,以上就是小编对于多线程的简单总结,学习还在继续,期待分享吧!