多线程入门第一记

时间:2022-12-28 11:12:43

目录:

  • 线程的定义
  • 多线程的数据安全问题
  • 创建多线程的两种基本方式

正文:

线程的定义

想知道什么是线程先要知道什么是进程,进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,这么讲不好理解,举例来说就是,运行QQ,就开启了一个进程,关闭QQ就是关闭一个进程,即是说,进程是一个执行中的程序。程序未打开时和文件没两样,打开之后,就和处理器产生了关联,成为了一个活动的实体,称之为进程。

至于线程,线程是进程的最小执行单元。以QQ举例来讲,运行的QQ是进程,具有多个功能,例如下载文件、听音乐、窗口聊天,正是因为系统支持多线程,你可以下文件的同时跟别人聊天。如果不支持多线程的话,会存在什么情况,情况就是在一段时间里面内进程只可以做一件事情,试想你下载文件的时候不能聊天,这时候你就会发现没有多线程是何等的握草了。

但是,话锋一转,我们都知道,cpu只有一个,多线程并不代表cpu同一时间服务于多个线程,而是各线程在调度算法的作用下轮流占用cpu,即是说,单个线程并没有一直占用cpu,即处于运行状态,而是隔一段时间使用一会儿cpu,隔开的这段时间就是cpu给其他线程使用的时间。

而之所以你感觉不到它是执行一会儿停一会儿,是因为,你是在是太慢了,你的反应速度完全达不到感知到它的一停一跑。就想像电影一样,明明是一帧一帧播放的,你却感受不到不连贯,而是觉得是一个连续不断的画面。

线程的数据安全

线程的好处毋庸置疑,但存在一个显著的数据安全问题,举个例子,两个吃苹果线程不停轮流吃一堆苹果直到苹果数变为0就停止,吃这个动作可分为两步,判断苹果数是否大于0,如果大于0就吃掉一个(把苹果数字减一),否则就跳出循环。

大家认为最后储存苹果的值会变成多少呢?讲道理肯定是0对不对,因为每次执行吃这个动作的条件都是苹果数量大于0,如果某线程吃完了最后一个苹果,另一个线程执行的时候会发现苹果为0了,就会跳出循环,不会再继续了,这逻辑可谓毫无瑕疵,是的毫无瑕疵,才怪了。

真实情况是你会发现即使是某一个线程把苹果数减为0之后,另一个线程还是会执行把苹果数量减一的操作,也就是说苹果数很有可能最终不是0,而是-1这样子。

为什么呢?原因出在多线程机制里面。这两个吃苹果线程都需要获得cpu执行权才能执行自己的吃苹果方法。但执行权就一个,僧多肉少,僧和僧就是竞争关系了。线程和线程也是竞争关系,也正因为是竞争,你的竞争线程可不会等你完成整个吃苹果的流程才来抢占CPU使用权。

要理解这个问题我们要从流程出发,给两个线程取名A、B,用A为例回顾下吃苹果流程:第一步、A检查苹果数量是否大于0,第二步、需要根据第一步判断结果,来决定如何操作,如果第一步判断得苹果数大于0,A就把苹果数字减一,否则什么也不做。我们带入一个具体场景来看,假如还剩一个苹果,恰好A抢到了执行权,嘿嘿,美滋滋。A刚刚执行完第一步,B就把A挤走了,自己占用了执行权,并且顺利的完成了判断和吃苹果的工作。此时变成苹果数变成0,B线程结束,执行权回到A手中,此时A是从它停止的的地方开始执行的,也就是第二步,即A会将苹果数再次减一,这时候就变成-1了。数据安全问题就出现了。

整理下思路,因为A判断的时苹果数是正常的,但是判断完被B抢走执行权,使苹果数变成0,B线程顺利结束,但此时A还剩下第二步将苹果数减一没执行,会继续执行,所以苹果数就变成-1了,这可不是我们想要的结果。

线程安全问题就是这样产生的,对于线程共享的数据来说,一些需要这些共享数据满足一定条件才执行的代码,在执行过程中,线程控制权被抢,而抢走控制权的线程改变了共享数据(即此例中的苹果),待原线程重获控制权,此时的共享数据因为被更改已经不满足执行条件了,但是该线程依然是从上一次执行到的位置继续执行(也就是满足条件才会执行的方法体中的某个位置),继而产生糟糕的后果。


线程创建的方式

常规来讲,创建线程有两种方式。

i.继承Thread类

第一步:自定义一个类(笔者取名为MyThread)继承系统类Thread

第二步:在自定义类中重写Thread类的run方法(线程的工作就是执行run方法)

第三步:调用默认构造函数创建一个自定义类的实例,并调用该实例的start方法,该线程即开启

源代码:

多线程入门第一记

多线程入门第一记

ii.实现Runnable

第一步:自定义一个类(笔者取名为MyInterfaceThread)实现系统接口Runnable

第二步:在自定义类中重写Runnable接口的run方法

第三步:调用MyInterfaceThread类的默认构造函数,创建一个MyInterfaceThread实例 interfaceThread

第四步:调用带参构造函数,创建一个Thread类的实例thread,参数就是第三步创建的 interfaceThread

第五步:调用thread的start方法,即线程开启

源代码:

多线程入门第一记

多线程入门第一记