第十六章 Java线程之初步认识

前言

我常常思考,为什么有的小朋友,语文好数学不好?有的数学好语文不好?这两者是相悖的吗?
语文往往充满了故事性,生动且丰富。数学往往充满了逻辑和单调,重复的数字可能让不爱数学的小朋友眼花缭乱。从这个问题延伸出来,有的人喜欢看故事,一整夜不眠不休都可以,但是在学习时往往喜欢打瞌睡。
所以我想,学习如果是枯燥的,那肯定让人觉得乏味。程序员是一个需要不断学习的行业,如果遇到学习就打瞌睡怎么办?因此,我总结了一些经验,将学习这件事和故事性结合起来,学习就不再会感觉很枯燥。
比如对于本篇研究文章的主题Thread类,线程。如果只是去看它的源码,方法,构造器,又没有实际业务需求的情况下,可能会很枯燥。但是如果以故事性的思维来学习线程,可能有意思多了。

线程的起源

在最开始的时候,只有进程,没有线程。计算机发展之初,程序是静止的,而程序的运行过程没有具体的表达,于是进程出现了。进程代表的是程序的运行过程,因此进程具有生命周期和不同的状态。
每个进程都会被分配一定的内存、程序控制块等等资源。在分配之初是一个构建进程的过程,相当于程序运行前期的准备,这个过程称为初始状态,准备完后称为就绪状态,意思是该进程以及准备好了,随时等待着CPU的召唤。在CPU的运行过程中,称为运行状态,但是在多道程序设计中,一个进程不能霸占着CPU,得让其他进程轮流来用,于是当进程被某些事件打断后退出CPU后,称为等待状态。等待状态不能直接进入运行状态,必须先进入就绪状态后才能等待CPU的召唤,这个过程不断循环直到进程运行完成后,称为进程的结束状态。

在这里插入图片描述

大概画了一个篮球场来简单表示下。篮球场上红队有5个人,还有4个候补人员,但是只有6件篮球队服。所以一个队员的运行状态应该是这样的。
1. 先穿好队服,进入就绪状态
2. 进入球场上打球,进入运行状态
3. 轮换下场,进入等待状态,但是得脱了队服。
4. 依次循环直到结束。
不同的是,在计算机中,进程有很多个,可能同时都在就绪状态,这需要等待CPU通过算法召唤,而非图中的排队模式。
在程序运行过程中,需要不断切换,由于进程拥有着大量资源,因此切换的代价变得高昂,进程也被称为重量级的进程。为了改进这个状况,于是线程出现了。
线程是一种轻量级的进程,一个进程中可以拥有多个线程,它降低了进程状态切换中带来的开销,并且拥有进程的作用,在一个进程中,所有线程共享该进程的资源,同时它也有进程具备的所有状态。

线程的状态 State

常用线程状态有三种:就绪、运行、等待。
为了描述线程的完整性,又增加了起始和结束两种状态。
最初的计算机,线程都是在内存中运行,但是内存往往不够用,于是在磁盘上增加了虚拟内存的概念。一些程序不需要或者不能被加入内存中时,就会被放入虚拟内存,这时程序处于挂起状态,虚拟内存其实就是在外存上。所以整个变成了下列六种:

初始、就绪、运行、等待、挂起、结束

程序在运行中的状态就被细粒的分为了上述的大概六种。那么如何来控制线程的状态呢,或者根据线程的状态来进行一些行为。
这就是程序员要做的事了,不同的语言提供了不同的控制方法,就java来说,Thread是线程的一个抽象定义。围绕着线程不同的状态转换提供了许多方法。这些方法可能是让就绪状态转为运行状态,如start()方法。
Thread对象中定义了许多的方法,这将在下一章做具体的研究。不同的方法将会使线程由哪个状态转换到哪个状态。

多线程

在计算机的发展过程中,CPU的速度变得原来越快,远远超过了其他硬件的速度,虽然高速缓存的设计让CPU使用率得到了一些改善,但是CPU的使用率依然很低。所以计算机大佬们想到了多道程序设计,让不同的进程轮流使用CPU,既提高了CPU的使用率,也让计算机可以同时处理多个进程任务,比如既可以玩游戏,也可以聊天。
多道程序的设计让计算机效率变得更高了,但是多道程序也有缺点。
在现实生活中,线程数量往往远超cpu内核数量,因此避免不了一核多线程轮流使用。将CPU分为时间片,每个线程轮流争取时间片,但是这个过程是不稳定的。一个线程如果独占一个CPU,运行完成所花费的时间是最快的,多线程的介入,让单个线程的运行时间变得更长了,哪怕只有几毫秒。如果在一些要求非常严格的领域中,如导弹、航天等等,这些时差将会是致命的。
因此,多线程提高了计算机的效率,同时也增加了单个线程的运行时长。

那么在多线程环境下,如何让这种不稳定性,相对变得稳定一些呢?
于是,线程优先级诞生了。

线程优先级 Priority

在时间片的概念上,每个线程获取时间片都是不稳定的,为了让一些重要的线程更快的执行完程序,就需要获取到更多的时间片。线程优先级就是为了实现类似的目的。
在JAVA中,线程优先等级一共有10级,高等级优先于低等级。从第1级开始到第10级,线程优先等级逐渐升高。第10级为最高优先级。初始创建的Thread线程对象默认设置为第5级。如果每个线程都不单独设置优先等级,理论上说,它们抢得时间片的概率是一样的。
虽然可以设置一个线程的优先级,但是当系统中的线程越来越多了,它们就会有一些物以类聚,人以群分的特征,不论是业务上的联系还是作用相似。为了更好的管理这些具有一定特征的线程,提出了线程组的概念。

线程组 ThreadGroup

线程组,是一个管理线程的类,或者理解为多个线程的线程管理者。当工厂里的工人越来越多,老板将不再会一一管理这些工人,而是招聘人事。人事小姐姐将会管理这些工人,包括考勤、工资、请休假等等。线程组的概念也是基于这个概念,它就相当于人事小姐姐。
在JAVA中,它提供了很多方法,可以让调用者通过人事小姐姐( 线程组)统一操作这些线程。比如设置这一组的优先级、中断这一组的所有线程等等…
每一个ThreadGroup都管理着一些线程,它具体的构造和结构将会在后面详细分析。

ThreadLocal

这是什么?为什么被设计出来?有什么优缺点?怎样避免缺点?
尽管网络上有许多相同、相似或者不同的答案。在我的理解中,ThreadLocal的出现只是解决了参数传递的问题,并不能解决并发共享变量。使用ThreadLocal可以传递不同层次间的公共变量,类似于一个线程生命周期内的session。

线程池

由于线程的创建和销毁都会消耗一定的cpu,在一定业务场景下,比如频繁调用创建和销毁线程的业务中,计算机的cpu会被大量消耗以至于影响程序的性能。而且在这种业务场景下,是不必这样做的。线程池的概念就是基于线程复用的原理,在线程1被A用户创建后,马上又来了B用户,这时线程1可以不用被销毁,继续服务于B用户。如果不采用这种理念,那么一个用户就会创建和销毁一个线程,增加了创建和销毁的次数,从而影响整个系统的性能。
线程池基于复用原理,可以根据不同的业务场景设置初始线程数,备用线程数,最大线程数等等,以及销毁机制。

总结

后续将逐渐分析线程、线程组、线程池的原理、使用场景、调优机制等等。

猜你喜欢

转载自blog.csdn.net/weixin_43901067/article/details/105413648
今日推荐