Java从入门到精通十二(java线程)【上】

简介: 计算机操作系统的有关线程和进程的浅显说明按照操作系统的理解,进程是操作系统分配资源的基本单位。线程是调度资源的基本单位。有很多形象的比喻,其实还是抽象化了。抽象化隐藏细节,实现似乎具体的可观。这样就是帮助理解。是在软件层次上的理解。把一个进程比作一个车间,然后车间的工人就是就相当于线程,然后其实多线程的化,你就向大的级别进行,多线程就是相当于一个大厂,然后大厂里的多个车间就是进程。进程是车间,车间的资源提供给线程进行共享。然后执行每个零碎的任务。

计算机操作系统的有关线程和进程的浅显说明

按照操作系统的理解,进程是操作系统分配资源的基本单位。

线程是调度资源的基本单位。


有很多形象的比喻,其实还是抽象化了。抽象化隐藏细节,实现似乎具体的可观。这样就是帮助理解。是在软件层次上的理解。


把一个进程比作一个车间,然后车间的工人就是就相当于线程,然后其实多线程的化,你就向大的级别进行,多线程就是相当于一个大厂,然后大厂里的多个车间就是进程。进程是车间,车间的资源提供给线程进行共享。然后执行每个零碎的任务。


一种非常标准的说法就是,进程就是程序的一次执行。打开一个程序,然后一个或者多个进程就开始了。然后这个程序具体功能的实现还是需要线程去具体实现的。


尽管这样说明,但是其实相信好多人还是和我有好多的疑问。因为根本的底层我们是不太理解的,或者是不理解。我的模电数电,组成原理没学好。汇编,操作系统的系统支撑了我的一些二对于底层的模糊认识。


在电脑上,我们在任务管理器可以直接查看进程。

所以说这些执行程序都可以认为是进程,基本的执行进程下面都有相应的线程。



还可以设置优先级



还可以设置哪些处理器运行指定的进程。



我的cpu不是八核的,这里是虚拟出来的,实际上只有四个内核。因为是intel,采用了超线程技术。



有的进程执行的时候只含有一个线程。举个例子。栗子很多,那么你到底想吃哪个?

开启一个终端,就是开启这样一个cmd.exe。cmd.exe是比较简单的程序,所以只需要这样一个线程。这也说明了它的功能就是比较单一的。等待用户输入命令,然后执行。



但是并不是一直一个,我在观察的时候会发现有时候会出现四个线程。我观察到,过一会儿会有规律的变成一个线程,即使我在终端执行命令,有时候它还是一个线程。


上面我们说了,线程就像车间忙碌的工人,大一点的程序一定会有多条线程的。


这样告诉我线程数就完了吗?我还想比较直观的看到线程的参数,以及优先级,我还想看到它的状态。


系统是一定会让你在终端查看进程的。我要先看进程。为什么我要先看进程,因为我可以通过进程的标识,查看它下面对应的进程。




提供一些参数


Pri:优先级

Thd:线程数

Hnd:句柄数

VM:虚拟内存

WS:工作集

Priv:专用虚拟内存

Priv Pk:专用虚拟内存峰值

Faults:页面错误

NonP:非页面缓冲池

Page:页面缓冲池

Cswtch:上下文切换


简单了解一下就可以,来举这个例子就是为了直观看到这些线程。感受一下。操纵系统中常说的阻塞或者排队状态,上面也给出了标识。


页面错误在组成原理提到过


若进程欲访问的页面目前并未驻留在内存中,将会发生页面错误


页面错误很正常,这里代表缺页。当发生缺页后,就会触发页面错误

当你的进程某些数据的页面长时间不被访问后,Windows将把它们从内存置换到硬盘上,常用的那些页面则常驻内存。但是需要用到那些数据的时候,发现内存中没有那些页面,就发生缺页中断,然后从硬盘上把那些页面调入。


我清楚的记得组成原理有提到过。果然听听还是有很大用的。


java执行方面的进程和线程的体现

我们会想到java基本的运行机制



java源代码首先需要通过java编译器编译为字节码文件(.class文件),字节码文件是一种二进制的文件,里面的数据紧密相连。文件的内容比较紧凑。字节码文件时通过javac .exe生成,然后再java.exe的作用下进行启动虚拟机(jvm),执行字节码。虚拟机会将编译好的字节码文件加载到内存(也就是类加载。然后虚拟机会对这个类加载的文件进行解释执行。)

直接由虚拟机进行解释执行,而并非操作系统。这样特点也决定了它的跨平台(java程序通过jvm实现)。


jvm也是一个软件层次的程序,在功能的实现上,具有自己完整的堆栈存储区,数据存储区这些,计数器等等。因此有时候也可以被认为是一个小型的计算机。但是这些都是虚拟出来的,实在软件的层次上虚拟出来的功能架构。


jvm是用来具体执行java程序的。jvm执行的时候本身也是一个进程的的进程。并且它是一个多线程对我进程。因为jvm需要做一些事情来支持java的运行机制。


所以jvm本身就是一个多线程的应用。从我们执行程序的main方法入口开始。jvm程序需要执行的时候,操作系统将jvm从磁盘存储器将其调入到内存中,然后创建一个jvm的进程。jvm启动主线程,主线程调用类的main方法,所以主线程也就是从main方法这里开始执行了。既然是一个多线程的应用,那么除了主线程以外还有其它的线程。


比如比较经典的垃圾回收机制的GC thread,此类线程主要进行对JVM里面的垃圾进行收集。再比如字节码编译的线程Compiler threads,再比如给jvm收集信号的线程Singal dispatcher thread。


好奇线程是如何调用的,实现的底层是什么?这就给自己又添麻烦了。


自己去查看start()这个启动的源码



这个函数的里面调用的另一个函数start0才应该是真正启动线程的。底层还是c++写的。native修饰说明是调用了原生系统函数。我去看了看,觉得还是挺复杂的。用notepad打开比较快一点



一个jvm.cpp就将近四千行代码,所以我大致看了几个函数就走了。再见!


参考了网上的一些说明,大致都是在说明方法调用逻辑。


线程启动过程。这是在c++源码上来说的。



说明:该图来自

小傅哥

CodeGuide | 程序员编码指南 Go! - 沉淀、分享、成长,让自己和他人都能有所收获!


此文有对源码,以及具体执行过程的一些探索。


java自己携带了一个可视化的工具,可以动态的观察一下cpu占用,堆,线程的情况。当然这些不是目前主要去看的,主要用在后面的性能调优上面。


VisualVM,java自带的一个监控工具


主要可以分析数据,跟踪内存泄漏,监控垃圾回收,执行内存等等的操作。


直接在控制台运行jvisualvm,大体就是这个界面。当然可以去进行远程连接,查看资源情况等等。





当你在运行一个程序的时候,比较大的一个程序,可以去看看jvm的堆占用情况。


java线程

Thread类信息摘要

public class Threadextends Objectimplements Runnable

线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。


创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。


创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。


每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。


一:嵌套类说明


public static enum Thread.Stateextends Enum<Thread.State>


该类的枚举常量说明了线程的状态


BLOCKED

受阻塞并且正在等待监视器锁的某一线程的线程状态。

NEW

至今尚未启动的线程的状态。

RUNNABLE

可运行线程的线程状态。

TERMINATED

已终止线程的线程状态。

TIMED_WAITING

具有指定等待时间的某一等待线程的线程状态。

WAITING

某一等待线程的线程状态。


另外一个嵌套类


public static interface Thread.UncaughtExceptionHandler


这是一个接口


当 Thread 因未捕获的异常而突然终止时,调用处理程序的接口。


当某一线程因未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询该线程以获得其 UncaughtExceptionHandler 的线程,并调用处理程序的 uncaughtException 方法,将线程和异常作为参数传递。如果某一线程没有明确设置其 UncaughtExceptionHandler,则将它的 ThreadGroup 对象作为其 UncaughtExceptionHandler。如果 ThreadGroup 对象对处理异常没有什么特殊要求,那么它可以将调用转发给默认的未捕获异常处理程序。


二:主要的一些字段


static int MAX_PRIORITY

线程可以具有的最高优先级。

static int MIN_PRIORITY

线程可以具有的最低优先级。

static int NORM_PRIORITY

分配给线程的默认优先级。


优先级可以自己进行设定,有一个范围,也可以去获取运行线程的优先级。


三:构造方法


Thread()

分配新的 Thread 对象。

Thread(Runnable target)

分配新的 Thread 对象。

Thread(Runnable target, String name)

分配新的 Thread 对象。

Thread(String name)

分配新的 Thread 对象。

Thread(ThreadGroup group, Runnable target)

分配新的 Thread 对象。

Thread(ThreadGroup group, Runnable target, String name)

分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,并作为 group 所引用的线程组的一员。

Thread(ThreadGroup group, Runnable target, String name, long stackSize)

分配新的 Thread 对象,以便将 target 作为其运行对象,将指定的 name 作为其名称,作为 group 所引用的线程组的一员,并具有指定的堆栈大小。

Thread(ThreadGroup group, String name)

分配新的 Thread 对象。


四:方法


static int activeCount()

返回当前线程的线程组中活动线程的数目。

void checkAccess()

判定当前运行的线程是否有权修改该线程。

int countStackFrames()

已过时。 该调用的定义依赖于 suspend(),但它遭到了反对。此外,该调用的结果从来都不是意义明确的。

static Thread currentThread()

返回对当前正在执行的线程对象的引用。

void destroy()

已过时。 该方法最初用于破坏该线程,但不作任何清除。它所保持的任何监视器都会保持锁定状态。不过,该方法决不会被实现。即使要实现,它也极有可能以 suspend() 方式被死锁。如果目标线程被破坏时保持一个保护关键系统资源的锁,则任何线程在任何时候都无法再次访问该资源。如果另一个线程曾试图锁定该资源,则会出现死锁。这类死锁通常会证明它们自己是“冻结”的进程。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。

static void dumpStack()

将当前线程的堆栈跟踪打印至标准错误流。

static int enumerate(Thread[] tarray)

将当前线程的线程组及其子组中的每一个活动线程复制到指定的数组中。

static Map<Thread,StackTraceElement[]> getAllStackTraces()

返回所有活动线程的堆栈跟踪的一个映射。

ClassLoader getContextClassLoader()

返回该线程的上下文 ClassLoader。

static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()

返回线程由于未捕获到异常而突然终止时调用的默认处理程序。

long getId()

返回该线程的标识符。

String getName()

返回该线程的名称。

int getPriority()

返回线程的优先级。

StackTraceElement[] getStackTrace()

返回一个表示该线程堆栈转储的堆栈跟踪元素数组。

Thread.State getState()

返回该线程的状态。

ThreadGroup getThreadGroup()

返回该线程所属的线程组。

Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()

返回该线程由于未捕获到异常而突然终止时调用的处理程序。

static boolean holdsLock(Object obj)

当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。

void interrupt()

中断线程。

static boolean interrupted()

测试当前线程是否已经中断。

boolean isAlive()

测试线程是否处于活动状态。

boolean isDaemon()

测试该线程是否为守护线程。

boolean isInterrupted()

测试线程是否已经中断。

void join()

等待该线程终止。

void join(long millis)

等待该线程终止的时间最长为 millis 毫秒。

void join(long millis, int nanos)

等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

void resume()

已过时。 该方法只与 suspend() 一起使用,但 suspend() 已经遭到反对,因为它具有死锁倾向。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。

void run()

如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

void setContextClassLoader(ClassLoader cl)

设置该线程的上下文 ClassLoader。

void setDaemon(boolean on)

将该线程标记为守护线程或用户线程。

static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。

void setName(String name)

改变线程名称,使之与参数 name 相同。

void setPriority(int newPriority)

更改线程的优先级。

void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

设置该线程由于未捕获到异常而突然终止时调用的处理程序。

static void sleep(long millis)

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

static void sleep(long millis, int nanos)

在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

void start()

使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

void stop()

已过时。 该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。

void stop(Throwable obj)

已过时。 该方法具有固有的不安全性。有关详细信息,请参阅 stop()。该方法的附加危险是它可用于生成目标线程未准备处理的异常(包括若没有该方法该线程不太可能抛出的已检查的异常)。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。

void suspend()

已过时。 该方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。有关更多信息,请参阅为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?。

String toString()

返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

static void yield()

暂停当前正在执行的线程对象,并执行其他线程。


以上引用摘自javaapi


具体的使用说明的话,将说明总结一些比较常用的方法进行举例代码。


相关文章
|
23小时前
|
消息中间件 缓存 NoSQL
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
|
1天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
1天前
|
Java 开发工具 Windows
Java入门及环境变量
Java入门及环境变量
|
1天前
|
缓存 Java
【Java基础】简说多线程(上)
【Java基础】简说多线程(上)
5 0
|
2天前
|
Java API 调度
[AIGC] 深入理解Java并发编程:从入门到进阶
[AIGC] 深入理解Java并发编程:从入门到进阶
|
2天前
|
Java Nacos 开发者
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
Java从入门到精通:4.2.1学习新技术与框架——以Spring Boot和Spring Cloud Alibaba为例
|
2天前
|
前端开发 Java 测试技术
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
|
2天前
|
Java 程序员 数据库连接
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
Java从入门到精通:3.3.2性能优化与调优——内存管理篇
|
2天前
|
Dubbo Java 应用服务中间件
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
Java从入门到精通:3.2.2分布式与并发编程——了解分布式系统的基本概念,学习使用Dubbo、Spring Cloud等分布式框架
|
2天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句

热门文章

最新文章