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基础 ...
随机推荐
- java中的collection小结
Collection 来源于Java.util包,是非常实用常用的数据结构!!!!!字面意思就是容器.具体的继承实现关系如下图,先整体有个印象,再依次介绍各个部分的方法,注意事项,以及应用场景. ...
- Android Bitmap 全面解析(一)加载大尺寸图片
压缩原因:1.imageview大小如果是200*300那么加载个2000*3000的图片到内存中显然是浪费可耻滴行为;2.最重要的是图片过大时直接加载原图会造成OOM异常(out of memory ...
- Linux基础命令---get获取ftp文件
get 使用lftp登录ftp服务器之后,可以使用get指令从服务器获取文件. 1.语法 get [-E] [-a] [-c] [-O base] rfile [-o lfil ...
- zabbix之模板制作(memcache redis)
#:找一台主机安装redis和memcached(记得安装zabbix-agent) root@ubuntu:~# apt install redis root@ubuntu:~# apt insta ...
- ligerUI 关闭父弹窗JS报错问题 解决方法
1:调用父窗口某一个文件框,获取焦点, parent.window.document.getElementById("roleName").focus(); 2:关闭父窗口pare ...
- 【Java 多线程】Java线程池类ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工厂类
Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService.利用这两个类,可以创建 ...
- 基于阿里云ecs(centos 7) 安装jenkins
1. 安装好 jdk 2. 官网(https://pkg.jenkins.io/redhat-stable/)下载rpm包(稳定版): wget https://pkg.jenkins.io/redh ...
- matplotlib animation
import numpy as np from matplotlib import pyplot as plt from matplotlib import animation fig, ax = p ...
- 使用$.post方式来实现页面的局部刷新功能
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- python pandas 中文件的读写——read_csv()读取文件
read_csv()读取文件1.python读取文件的几种方式read_csv 从文件,url,文件型对象中加载带分隔符的数据.默认分隔符为逗号read_table 从文件,url,文件型对象中加载带 ...