当前位置: 首页 > news >正文

Java第二十章多线程

一、线程简介

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程可以并发执行。线程拥有自己的栈和局部变量,但是它们共享进程的其他资源,如全局变量、堆内存等。线程的优先级决定了线程需要时间片多少分配的线程属性。线程的启动和终止需要通过构造线程对象和调用start()方法来实现。

二、创建线程

1、继承Thread类

Thread类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread实例。Thread类中常用的两个构造方法如下:

1.public Thread():创建一个新的线程对象。

2.public Thread(String threadName):创建一个名称为threadName的线程对象。

继承Thread类创建一个新的线程的语法如下:

public class ThreadTest extends Thread{
}

完成线程真正的功能的代码放在类的run()方法中,当一个类继承Thread类后,就可以在该类中覆盖run()方法,将实现该线程功能的代码写入run()方法中,然后调用Thread类中的start()方法执行线程,也就是调用run方法。

Thread对象需要一个人物来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在run方法中。run()方法必须使用以下语法格式:

public void run(){
}

注意:start()方法调用一个已经启动的线程,系统将抛出IllegalThreadStateException异常。

当执行一个线程程序时,就自动产生一个线程,主方法正是在这个线程上运行的。当不再启动其他线程时,该程序就为单线程程序,如本章以前的程序都是单线程程序。主方法线程启动由Java虚拟机负责,程序员负责启动自己的线程。代码如下:

public static void main(String[] args){new ThreadTest().start();
}

例题:让线程循环打印1~10的数字

start()方法会启动线程,线程自动执行run()方法中的代码,就可以看到for循环打印的数字了。

在main()方法中,使线程执行需要调用Thread类中的start()方法,start()方法调用被覆盖的run()方法,如果不调用start()方法,线程永远都不会启动,在主方法没有调用start()方法之前,Thread对象只是一个实例,而不是一个真正的线程。

2、实现Runnable接口

例题:让窗口中的图标动起来

三、线程的生命周期

线程具有生命周期,其中包含 7 种状态,分别为出生状态、就绪状态、运行状态、等待状态、眠状态、阻塞状态和死亡状态。出生状态就是线程被创建时处于的状态,在用户使用该线程实例调用start0方法之前线程都处于出生状态;当用户调用 start()方法后,线程处于就绪状态 (又被称为可执状态);当线程得到系统资源后就进入运行状态。
一旦线程进入可执行状态,它会在就绪与运行状态下转换,同时也有可能进入等待、休眠、阻塞或死亡状态。当处于运行状态下的线程调用 Thread 类中的wait0方法时,该线程便进入等待状态,进入等待状态的线程必须调用Thread类中的notify()方法才能被唤醒,而调用notifvAll()方法可将所有处于等待状态下的线程唤醒;当线程调用 Thread 类中的sleep()方法时,则会进入休眠状态。如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时线程进入就绪状态,对于阻塞的线程来说,即使系统资源空闲,线程依然不能回到运行状态。当线程的run()方法执行完毕时,线程进入死亡状态。

四、操作线程的方法

1、线程的休眠

一种能控制线程行为的方法是调用sleep()方法,sleep()方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。在前面的实例中,已经演示过sleep()方法,它通常是在run()方法内的循环中被使用。sleep()方法的语法如下:

try{Thread.sleep(2000);
}catch(InterruptedException e){e.printStackTrace();
}

上述代码会使线程在2秒之内不会进入就绪状态。由于sleep()方法的执行有可能抛出InterruptedException异常,所以将sleep()方法的调用放在try-catch块中。虽然使用了sleep()方法的线程在一段时间内会醒来,但是并不能保证它醒来后进入运行状态,只能保证它进入就绪状态。

例题:每0.1秒绘制一条随机颜色的线条

        在本实例中定义了 getC()方法,该方法用于随机产生 Color 类型的对象并且在产生线程的匿名内部类中使用 getGraphics()方法获取 Graphics 对象,使用该对象调用setColor0方法为图形设置颜色。调用 drawLine()方法绘制一条线段,同时线段会根据纵坐标的变化自动调整。

2、线程的加入

        如果当前某程序为多线程程序,假如存在一个线程 A,现在需要插入线程 B,并要求线程B先执行完毕,然后再继续执行线程A,此时可以使用 Thread 类中的 join()方法来完成。这就好比此时读者正在看电视,突然有人上门收水费,读者必须付完水费后才能继续看电视。
        当某个线程使用 join()方法加入另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。下面来看一个使用 join()方法的实例。

例题:让进度条A等待进度条B

3、线程的中断

以往有的时候会使用stop()方法停止线程,但当前版本的JDK早已废除了stop()方法,不建议使用stop()=方法来停止一个线程的运行。现在提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

如果线程是因为使用了sleep()或wait()方法进入了就绪状态,可以使用Thread类中interrupt()方法使线程离开run()方法,同时结束线程,但程序会抛出InterruptedException异常,用户可以在处理该异常时完成线程的中断业务处理,如终止while循环。

例题:单击按钮停止进度条滚动

4、线程的礼让

Thread类中提供了一种礼让方法,使用yield()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。

yield()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时会再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用yield()方法,因为操作系统会为线程自动分配CPU时间片来执行。

五、线程的优先级

例题:观察不同优先级的线程执行完毕顺序

由于线程的执行顺序是由CPU决定的,即使线程设定了优先级也是作为CPU的参考数据,所以真实的运行结果可能并不一定按照优先级排序,例如笔者运行的结果如上。执行顺序为:D>C>B>A

六、线程同步

1、线程安全

实际开发中,使用多线程程序的情况很多,如银行排号系统、火车站售票系统等。这种多线程的程序通常会发生问题,以火车站售票系统为例,在代码中判断当前票数是否大于0,如果大于0则执行将该票出售给乘客的功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出票数大于0的结论,于是它也执行售出操作,这样就会产生负数。所以,在编写多线程程序时,应该考虑到线程安全问题实质上线程安全问题来源于两个线程同时存取单一对象的数据。
例如,在项目中创建ThreadSafeTest类,该类实现了Runnable接口,在未考虑到线程安全问题的基础上,模拟火车站售票系统的功能的代码如下:

2、线程同步机制

例题:开发线程安全的火车售票系统

同步方法就是在方法前面用synchronized关键字修饰的方法,其语法如下:
synchronized void f(){}
当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为synchronized,否则就会出错。

修改例20.7的代码,将共享资源操作放置在一个同步方法中,代码如下:

nt num = 10:
public synchronized void doit() {//定义同步方法if(num>0){try{Thread.sleep(10);}catch(InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"————票数"+num--);}
}
public void run(){while(true){doit();}            //在run()方法中调用该同步方法
}

相关文章:

  • Android自动化测试中使用ADB进行网络状态管理!
  • 游戏缺少d3dx9_43.dll修复方法分享,快速解决dll缺失问题
  • 浅学指针(3)
  • 宏定义中 ## 和 # 的作用
  • 中国信息通信研究院产业与规划研究所校招一面、二面内容
  • ChatGPT生成的一些有趣的文件管理用python小程序
  • Vue框架学习笔记——计算属性
  • 数据结构 | 二叉树的概念及前中后序遍历
  • 鸿蒙开发学习——应用程序框架
  • 增强静态数据的安全性
  • Java实现通过经纬度求两个任意地点在球面上的距离
  • java开发之个微群聊自动添加好友
  • 装饰者设计模式
  • vue3中的动态component组件
  • 前端命名规范总结
  • HTTP传输编码增加了传输量,只为解决这一个问题 | 实用 HTTP
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • jquery cookie
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 百度地图API标注+时间轴组件
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 基于Android乐音识别(2)
  • 记一次用 NodeJs 实现模拟登录的思路
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 利用DataURL技术在网页上显示图片
  • 区块链将重新定义世界
  • 双管齐下,VMware的容器新战略
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • ​​​​​​​​​​​​​​Γ函数
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2)STM32单片机上位机
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (Java岗)秋招打卡!一本学历拿下美团、阿里、快手、米哈游offer
  • (Java数据结构)ArrayList
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (ZT)薛涌:谈贫说富
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (附源码)ssm失物招领系统 毕业设计 182317
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (十一)图像的罗伯特梯度锐化
  • (四) 虚拟摄像头vivi体验
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (转)C#开发微信门户及应用(1)--开始使用微信接口
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • **python多态
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .gitignore文件_Git:.gitignore
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .naturalWidth 和naturalHeight属性,
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能