1、并发和并行
(1)并发:一个时间段有多个程序处于已启动运行到运行完毕之间,同一处理机上进行,会发生抢占,按一定的策略轮流进行。
(2)并行:有多个CPU,一个CPU执行一个进程,另外的CPU执行另一个进程,互补抢占CPU资源,可以同时进行。
2、线程和进程
(1)进程:资源分配的基本单元
==资源分配的基本单元,系统运行程序的基本单位,一个进程内有多个线程。进程是独立的。
(2)线程:CPU调度的基本单元
==执行的基本单元,能独立运行的基本单位,也是独立调度和分派的基本单位
==同一进程的多个线程共享进程的堆和方法区资源。同一进程内的多个线程都具有相同的地址空间(进程的地址空间),同一进程内的线程共享内存和文件,线程间的通信不用调用内核。
==每个线程有自己的程序计数器、虚拟机栈和本地方法栈(私有的)。同一进程间的线程会相互影响。
3、程序计数器(私有)
为了线程切换后能恢复到正确的执行位置。
(1)程序计数器依次读取指令,实现代码的流程控制(顺序执行、选择、循环、异常处理)
(2)多线程下,程序计数器用于记录当前线程执行的位置,从而当线程被切换时能够知道运行的指令的位置
4、虚拟机栈(私有)和本地方法栈(私有)
保证线程中的局部变量不被别的线程访问到
(1)虚拟机栈:执行Java方法(字节码)服务
==每个栈的栈帧用于存储局部变量表、操作数栈、常量池引用等信息。
==方法的调用和执行过程,对应着一个栈帧再虚拟机栈的入栈出栈过程。
(2)本地方法栈:执行native服务
5、堆和方法区(共享公有)
所有线程共享的资源
(1)堆:
==进程中最大的一块内存
==主要用于存放新创建的对象(分配内存)
(2)方法区:
==主要用于存放已被加载的类的信息、常量、静态变量、即时编译器编译后的代码等数据。
6、线程的生命周期和状态
(1)线程的生命周期并不是固定处于某个状态而是随代码的执行在不同状态间切换。
(2)new(创建):新建一个线程对象
(3)runnable(运行):Java线程中将ready(就绪)和running(运行中)称为runnable
== new 状态调用 _Thread.start()_方法后进入ready状态,等待线程调度并分配CPU使用权
== ready状态的线程获得了CPU时间片,开始执行代码,进入running状态
== running状态执行 _Thread.yield()_方法会回到ready状态
(4)blocked(阻塞):表示线程阻塞,没有获得锁的情况下(锁)
(5)waiting(等待):线程执行该对象的 _Object.wait()|Thread.join()_方法,进入waiting状态,需要其它线程进行通知 _Object.notify()|Object.notifyAll()_或中断才能解除该状态
(6)timed_waiting(超时等待):线程执行 Thread.sleep(long mills)| Object.wait(long mills)|Thread.join(long mills)_方法,增加了超时限制,进入timed_waiting状态,超时时间达到后,线程会自动返回 (自动调用Object.notify()|Object.notifyAll())_到runnable状态
(7)terminated(终止):线程执行 _Runnable的run()_方法后,会进入到terminated状态,线程执行完毕
7、sleep()\wait()\yield()\join()\state()\notify()\notifyAll()
(1)Thread.sleep():线程休眠 模拟倒计时、模拟网络延迟
(2)Thread.yield():线程礼让,running回到ready,让cpu重新调度,不一定礼让成功。
(3)Thread.join():线程插队,执行join()方法,原线程进入等待,插入的线程先执行
(4)Thread.state():观测线程转台,执行该方法后能够观测到线程的当前状态
(5)Object.wait():线程等待,进入等待waiting状态
(6)notify():唤醒当前等待线程
(7)notifyAll():唤醒所有等待线程
(8)sleep()和wait()的区别和共同点
==区别:
=====sleep()是Thread类的方法,没有释放锁
通常用于暂停执行
sleep()方法执行完成后,线程会自动苏醒
=====wait()是Object类的方法,会释放锁
通常用于线程间的交互或通信
wait()方法执行后,线程不会自动苏醒,需要别的线程调用同一个对象的notify()或notifyAll()方法。
wait(long timeout),超时后线程会自动苏醒
==共同点:都可以暂停线程的执行
(9)调用start()方法会执行run()方法,不直接调用run()方法?
==调用Thread.start(),自动执行run(),正确的多线程工作
====创建线程时,进入new,调用Thread.start(),会启动一个线程并使线程进入就绪状态,等待调度
====start()执行线程准备工作,会自动执行run()方法
==直接执行run(),不是多线程工作
====相当于直接把run()方法当作一个main()线程下的普通方法进行执行,不会在某个线程中执行它,不是多线程工作
8、线程优先级
(1)JVM采用抢占式调度模型,会给优先级高的线程分配CPU
(2)10个级别的线程优先级(1-10)
(3)当两个线程同时处于ready时,优先级越高越容易被调度
(4)默认优先级是5,Thread.NORM_PRIORITY
(5)最高优先级是10,Thread.MAX_PRIORITY
(6)最低优先级是1,Thread.MIN_PRIORITY
(7)设置优先级方法:Thread.setPriority()
例:Thread.setPriority(Thread.NORM_PRIORITY-1) 4
(8)获取当前的优先级:Thread.getPriority()
例:Thread.getPriority() 4
9、守护线程和用户线程
(1)守护线程Daemon Thread:gc()垃圾回收线程,虚拟机不要等待守护线程执行完毕
=监控内存线程、垃圾回收线程等 “后台线程”
(2)用户线程User Thread:main(),虚拟机必须等待用户线程执行完毕
(3)Thread.setDaemon():默认为false,表示用户线程,正常的线程都是用户线程
10、线程调度
(1)线程调度:给多个线程按照特定的机制分配CPU使用权的过程
==ready状态到running状态,给线程分配CPU的使用权,获得CPU使用权后才会进程running状态
(2)Linux线程调度
==Linux中线程调度是按照进程的调度方式来进行调度的
==调度器是基于线程的调度策略和静态调度优先级来决定哪个线程来运行
==3种调度策略:SCHED_OTHER 分时调度(默认)
SCHED_FIFO 实时调度,先进先出
SCHED_RR 实时调度,时间片轮转
(3)Windows线程调度
==基于优先级的、抢占式调度算法
(4)Java线程调度
==一个Java程序就是一个进程(单进程,多线程的Java)
==Thread类的方法声明为Native的
==JVM实现了一个线程调度器,定义了线程调度模型(协同式线程调度模型、抢占式调度模型)
=====协同式线程调度
1、 线程的执行时间由线程本身来控制,线程工作执行完成后,要主动通知系统切换到另一个线程上
2、好处:实现简单,切换操作对线程可知,没有线程同步问题
=====抢占式线程调度模型
1、多线程系统,每个线程由系统来分配执行时间
2、线程的切换由系统实现
3、优先级高的线程占用CPU,通过线程优先级设置可以设置线程的调度顺序,因为线程调度是取决于原生操作系统,优先级调度可能不太靠谱
11、上下文切换
(1)上下文:线程在执行过程中自己的运行条件和状态
(2)上下文切换:线程切换需要保存当前线程的上下文,等待再次调度时恢复现场,并加载下一个将要占用CPU的上下文的过程。
==让出CPU , 调用sleep()、wait()
==时间片用完
==调用阻塞类型的系统中断,请求IO
==被终止或结束运行
(3)上下文切换是操作系统的基本功能,每次徐娅保存信息、恢复信息,会占用CPU、内存等系统资源进行处理,效率会由损耗,频繁切换会造成整体效率地下。
12、创建线程的方式
(1)继承Thread类创建线程(无法获取执行结果)
继承Thread类并重写run()方法,new创建一个线程对象,调用其start()方法,可以启动一个线程,会运行run()中的代码。
Thread类实现了Runnable接口
(2)实现Runnable接口,可以避免多继承(无法获取执行结果)
实现Runnable接口,重写run()方法,new创建线程对象,调用其start()方法,可以启动线程,运行run()方法中的代码
(3)实现Callable接口
==Callable位于java.util.concurrent包下
==实现Callable接口,重写call()方法(相比run(),call()有返回值)
传入Callable任务给FutureTask对象(FutureTask可用于异步获取执行结果或取消执行任务的场景),FutureTask.get()方法可以异步获取执行结果。(适用于耗时的计算,可以在主线程完成任务后再获取结果,同时,多次调用run()方法,FutureTask只会执行 一次传入的任务)
new创建线程对象,调用其start()方法,可以开启线程
(4)线程池创建线程,直接调用ThreadPoolExecutor
==直接调用ThreadPoolExecutor构造线程
==线程池本身是一个HashSet,多余的任务会放到阻塞队列中。
==线程池创建线程方式很多,可以使用Executor静态工厂构建
但建议使用待遇ThreadFactory参数的ThreadPoolExecutor构造方法设置线程名字。
Original: https://www.cnblogs.com/hexiayuliang666/p/16154777.html
Author: 与长安故里
Title: 线程
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570756/
转载文章受原作者版权保护。转载请注明原作者出处!