Java高级程序设计笔记 • 【第2章 多线程(一)】
全部章节 >>>>
本章目录
2.1 线程的概述
程序的运行默认仅包含一个主线程,所有的工作都由主线程承担,当任务繁重耗时,会对于主线程产生很大压力,甚至引起阻塞卡顿
将繁重耗时的任务分出一些子线程去执行,相当于多人做一件事,能大大降低主线程的阻塞,同时提高程序运行效率
2.1.1 进程
几乎所有的操作系统都只是同时运行多个任务,一个任务通常只对应一个程序,每个运行的程序就是一个进程
当一个程序进入到内存中运行时,即变为一个进程。进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位
进程具有以下特点: 独立性、动态性、并发性
线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程
线程之间是独立运行,线程的执行是抢占式的,即当前任何运行的线程在任何时候都有可能被停止,被其他线程抢走对 CPU 的使用权
2.1.2 多线程优势
多线程来实现并发比使用多进程实现并发的性能要高,在执行耗时任务时,开启多个线程能提高执行效率
使用多线程编程具有以下优点:
- 进程之间不共享内存,但线程之间共享内存非常容易
- 创建线程代价较小,能提高任务执行效率
- Java 内置了多线程功能支持,大大简化了多线程编程实现
2.1.3 Thread 类
Java 使用 java.lang.Thread 类表示线程,所有的线程对象都必须是 Thread 类或其子类的实例
通过继承 Thread 类来创建并启动多线程的步骤:
- 定义类继承Thread 类,并重写该类的 run() 方法(run方法是线程的执行主体方法)
- 创建 Thread 子类的实例,即创建线程对象 调用线程对象的 start() 方法,启动该线程、
Thread 类常用方法
方法名 |
作用 |
void setName(String name) |
设置线程的名字 |
String getName() |
返回当前线程的名字 |
Thread currentThread() |
返回当前正在执行的线程对象 |
void start() |
启动线程 |
String getAbsolutePath() |
返回此 File 对象所对应的绝对路径名 |
void run() |
线程一旦执行,便调用此方法 |
boolean isLive() |
判断当前线程是否活跃,活跃返回 true,否则,返回 false |
Thread类实现多线程
示例:实现步骤1
- 创建类继承Thread类,并且重写run方法
- run方法中完成线程执行任务的代码
public class ThreadSimple extends Thread {
private int i = 0;
// 重写 run() 方法
public void run() {
for (; i < 50; i++) {
// getName() 用于获取当前线程的名字
System.out.println(this.getName() + "\t" + i);
}
}
注意:
- 进行多线程编程时,Java 程序运行时默认的主线程即为 main() 方法体,该方法就是主要线程的线程执行体
- 使用继承 Thread 类的方式创建线程类时,多个线程之间无法共享线程类的实例变量(案例线程类中声明的变量i无法被多个线程共享使用)
2.1.4 实践练习
2.2 Runnable接口
2.2.1 Runnable接口
通过继承Thread类虽然可以实现多线程,但是无法实现多线程之间的资源共享,通过Runnable接口实现多线程可以很好解决这个问题
Runnable 接口的使用步骤:
- 定义 Runnable 接口的实现类,并重写该接口的 run() 方法为线程执行主体
- 创建 Runnable 实现类的实例,并以该实例作为 Thread 实例操作的目标
- 创建 Thread 类对象时传入实现Runnable接口的类对象
说明:Runnable对象仅作为 Thread 类对象操作的目标对象,而实际的线程对象依然是 Thread 类的实例
2.2.2 使用 Runnable接口实现多线程
示例:实现步骤1
- 创建类实现Runnable接口,并且重写run方法
- run方法中完成线程执行任务的代码
public class RunnableSimple implements Runnable {
private int i = 1;
// 重写 run() 方法,run() 方法同样是线程执行体
public void run() {
for (; i <= 20; i++) {
// 当实现 Runnable 接口时,获取当前线程,只能使用 Thread.currentThread() 方法
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
示例:实现步骤2
- 在测试类的main方法中,创建Runnable子类实例对象,作为线程对象执行的目标
- 创建Thread类实例,传入Runnable子类对象(这里可以多个线程使用同一个目标)
- 调用start()方法启动线程
// 创建 RunnableSimple 实例
RunnableSimple rs = new RunnableSimple();
// 创建 Thread 线程实例,该线程操作 rs 对象
Thread thread = new Thread(rs);
thread.setName(“ 线程 A”); // 设置第一个线程的名字
// 创建第 2 个 Thread 线程实例
Thread thread_02 = new Thread(rs);
thread_02.setName(" 线程 B");
// 启动线程 A
thread.start();
// 启动线程 B
thread_02.start();
2.2.3 Thread和Runnable
Thread 类和 Runnable 接口都可以实现多线程,但是Thread方式的子类直接就是线程类,实例化后直接启动;
而Runnable方式创建的类仅为线程的执行提供目标,最终还是依靠Thread类创建线程对象
Runnable方式的优势:
适合多个相同程序代码的线程去处理同一个资源
避免 Java 特性中的单根继承限制、更能体现面向对象特点
增加程序健壮性。在数据被共享时,仍然可以保持代码和数据的分离
经验:在实现多线程时,更推荐大家使用 Runnable 接口
2.2.4 实践练习
2.3 控制线程
2.3.1 线程休眠
Thread 类的 sleep(long millis) 方法可以让当前正在执行的线程暂停多少毫秒,并进入阻塞状态
调用 sleep() 方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会
示例:使用 sleep() 方法休眠线程,实现每隔 500 毫秒出现一颗五角星
public void run() {
// 循环 20 次,打印出 20 颗五角星
for (int i = 0; i < 20; i++) {
try {
// 线程休眠 500 毫秒
Thread.sleep(500);
System.out.print(" ☆ ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.3.2 join线程
Thread 类的 join() 方法可以让一条线程等待另一条线程完成
如:在线程 B 中,调用了线程 A 的 join() 方法后线程 B 则会被挂起,并进入阻塞状态,直到线程 A 执行完毕后才会继续执行线程 B
示例:实现两个线程之间等待效果
分析:实现步骤
- 创建线程类A继承Thread类,重写run方法
- 创建线程类B继承Thread类,B类中需要声明线程A类的对象引用,以便在B类中可以使用线程A类对象
- 线程B类run方法中,调用线程A对象的join()方法,此时线程B会处于等待状态,直到线程A执行完毕
- 创建测试类,创建线程A和B,分别线程A、B并运行
2.3.3 实践练习
2.4 线程的生命周期
当线程被创建启动后,该线程并非一启动就会被执行,也不会一直处于执行状态。在线程的生命周期中,要经过新建>>就绪>>运行>>阻塞>>死亡这 5 种状态
注意:多线程并非同时执行,而是CPU在多个线程之间来回快速切换执行,而线程本身也在运行和阻塞状态之间切换
2.4.1 新建和就绪状态
当程序使用 new 关键字创建线程后,该线程就处于新建状态
当线程对象调用了 start() 方法后,该线程就处于就绪状态,处于就绪状态的线程 并未开始运行,仅表示该线程可以运行,该线程何时开始运行,取决于 JVM中线程调度器的调度
提示:启动线程使用 start() 方法,而不是 run() 方法 永远不要调用线程对象的 run() 方法
2.4.2 线程运行状态
如果处于就绪状态的线程获得了 CPU,开始执行 run() 方法的线程执行体,则该线程处于运行状态
对于单CPU的机器,任何时刻只有一个线程处于运行状态,它会在多个线程之间来回切换执行
对于多CPU的机器,将会有与 CPU 个数相同的线程并行执行
2.4.3 线程阻塞状态
当发生如下情况时,线程将进入阻塞状态:
- 线程调用 sleep() 方法主动放弃占有的 CPU 资源
- 线程调用了一个阻塞式的 I/O 方法,在方法之间,该线程被阻塞
当发生如下特定的情况时,可以解除上述阻塞,让该线程重新进入就绪状态:
- 调用 sleep() 方法的线程经过了指定的时间
- 线程调用的阻塞式 I/O 方法已经返回
2.4.4 线程死亡状态
线程会以如下方式结束,结束后就处于死亡状态
run() 方法执行完成,线程正常结束
线程抛出一个未捕获的 Exception 或 Error
直接调用线程的 stop() 方法来结束该线程,通常不推荐使用该方法
提醒:不要试图对一个已经死亡的线程调用其 start() 方法使其重新启动,否则会抛出异常
2.4.5 线程执行状态图
2.4.6 实践练习
总结:
- 进程一般代表一个应用程序,一个进程中可以包含多个线程
- 合理使用多线程能够提高程序的执行效率,处理高并发应用
- 线程的创建有继承Thread类和实现Runnable接口两种方式,通过Runnable方式可以更加容易实现多线程之间资源共享
- 通过sleep可以使线程进入休眠状态,通过join方法可以让线程处于等待,其他线程执行完毕后继续执行
- 线程生命周期包括:新建 就绪 运行 阻塞 死亡5种状态
Java高级程序设计笔记 • 【第2章 多线程(一)】的更多相关文章
- Java高级程序设计笔记 • 【目录】
持续更新中- 我的大学笔记>>> 章节 内容 实践练习 Java高级程序设计作业目录(作业笔记) 第1章 Java高级程序设计笔记 • [第1章 IO流] 第2章 Java高级程序设 ...
- Java高级程序设计笔记 • 【第3章 多线程(二)】
全部章节 >>>> 本章目录 3.1 同步代码块 3.1 线程安全 3.1.1 模拟银行取款 3.1.2 同步代码块的使用 3.1.3 实践练习 3.2 同步方法 3.2. ...
- Java高级程序设计笔记 • 【第4章 网络编程】
全部章节 >>>> 本章目录 4.1 网络基础知识 4.1.1 IP地址 4.1.2 端口号 4.1.3 使用InetAddress 4.1.4 InetAddress 类 ...
- Java高级程序设计笔记 • 【第6章 设计模式】
全部章节 >>>> 本章目录 6.1 设计模式 6.1.1 设计模式概述和分类 6.1.2 单列模式介绍 6.1.3 单例模式的实现 6.1.4 实践练习 6.2 单例模式 ...
- Java高级程序设计笔记 • 【第5章 XML解析】
全部章节 >>>> 本章目录 5.1 XML 文档概述 5.1.1 XML文档结构 5.1.1 XML结构说明 5.1.1 XML文档元素 5.1.2 XML文档语法规范 ...
- Java高级程序设计笔记 • 【第1章 IO流】
全部章节 >>>> 本章目录 1.1 File类访问文件 1.1.1 File 类 1.1.2 File 类方法 1.1.3 实践练习 1.2 文件过滤器 1.2.1 Fi ...
- Javascript高级程序设计笔记 <第五章> 引用类型
一.object类型 创建object实例的方式有两种: //第一种使用new操作符跟构造函数 var person= new Object(); person.name="小王" ...
- Java高级程序设计作业目录(作业笔记)
持续更新中............. Java高级程序设计笔记 • [目录] 我的大学笔记>>> 第1章 IO流>>> 1.1.3 编写Java程序,在电脑硬盘里, ...
- Java Web程序设计笔记 • 【目录】
章节 内容 实践练习 Java Web程序设计作业目录(作业笔记) 第1章 Java Web程序设计笔记 • [第1章 Web应用程序] 第2章 Java Web程序设计笔记 • [第2章 JSP基础 ...
随机推荐
- Docker学习(三)——Docker镜像使用
Docker镜像使用 当运行容器时,使用的镜像如果在本地中不存在,docker就会自动从docker镜像仓库中下载,默认是从Docker Hub公共镜像源下载. 1.镜像使用 (1)列 ...
- Fragment放置后台很久(Home键退出很长时间),返回时出现Fragment重叠解决方案
后来在google查到相关资料,原因是:当Fragment长久不使用,系统进行回收,FragmentActivity调用onSaveInstanceState保存Fragment对象.很长时间后,再次 ...
- UIImageView总结
UIImageView UIKit框架提供了非常多的UI控件,但并不是每一个都很常用,有些控件可能1年内都用不上,有些控件天天用,比如UIButton.UILabel.UIImageView.UITa ...
- 【Java 8】Stream中的Pipeline理解
基于下面一段代码: public static void main(String[] args) { List<String> list = Arrays.asList("123 ...
- lucene的索引查询
package com.hope.lucene;import org.apache.lucene.document.Document;import org.apache.lucene.document ...
- win10更新后任务栏卡死 的原因和解决办法
@ 目录 现象: 原因: 第一步:断网并关闭资讯和兴趣 第二步:卸载更新 第三步:关闭win10自动更新 第四步:永久关闭资讯和兴趣 现象: win10 更新后,开机任务栏卡死,点开始反应,设置页面无 ...
- .NET内存性能分析宝典
.NET Memory Performance Analysis 知道什么时候该担心,以及在需要担心的时候该怎么做 译者注 **作者信息:Maoni Stephens ** - 微软架构师,负责.NE ...
- 摘要任务工期计算(Project)
<Project2016 企业项目管理实践>张会斌 董方好 编著 先说一个好消息:摘要工期是可以自动计算的. 比如A1.A2.A3.A4四个任务,工期如下图安排: 而他们的摘要任务,就不再 ...
- k8s daemonset controller源码分析
daemonset controller分析 daemonset controller简介 daemonset controller是kube-controller-manager组件中众多控制器中的 ...
- java 网络编程基础 TCP/IP协议:服务端ServerSocket;客户端Socket; 采用多线程方式处理网络请求
1.Java中客户端和服务器端通信的简单实例 Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一 ...