当前位置:网站首页>day12_多线程
day12_多线程
2022-07-29 05:26:00 【胖叔讲Java】
java多线程上
学习目标
1.线程相关概念
2.熟练创建多线程
3.熟练理解线程的生命周期
一、多线程相关概念
程序 可以完成特定任务用计算机语言编写的一组指令的集合。
**进程 ** 正在运行的程序;如:运行中的360,运行中的微信钉钉等。

线程 进程可进一步细化为线程,是一个进程的最小执行单位。
- 若一个进程同一时间并行执行多个线程,就是支持多线程的
- 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小 。
- 一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一堆中分配对象,可以 访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
- 一个进程由多个线程组成;有且只有一个主线程,可以有多个子线程;由主线程领导其他子线程工作。java程序的主线程是main方法所在线程。
单核CPU和多核CPU
- 单核CPU,在某一段时间内只能执行一个线程,只是切换线程的速度特别快,看起来好像多个线程一起执行。
- 如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
**并行与并发 **
- 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
- 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。
二、多线程的创建与使用
JDK5之前创建新执行线程有两种方法:
- 继承Thread类的方式
- 实现Runnable接口的方式
2.1 继承Thread类创建子线程
我们查看Thread类api,查看如何创建子线程

通过查看api,总结创建子线程的步骤为:
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法
示例1:主线程和子线程同时打印1-100
/** * 主线程和子线程同时打印1-100 */
public class ThreadDemo1 extends Thread{
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
//让程序运行慢点,停留100毫秒后在执行
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//this.getName()获得线程名字
System.out.println(this.getName()+":"+i);
}
}
public static void main(String[] args) {
//1.创建子线程
Thread thread = new ThreadDemo1();
//2.运行子线程中的run方法代码
thread.start();
//3.主线程运行代码
for(int i=1;i<=100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
2.2 使用线程的两个注意点
创建线程需要注意以下两点:
- 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。想要启动多线程,必须调用start方法 。 start()方法由 JVM 调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
- 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常
IllegalThreadStateException;非法的线程状态
示例2:使用线程的两个注意点
public class ThreadDemo2 extends Thread{
@Override
public void run() {
System.out.println("子线程");
}
public static void main(String[] args) {
//1.创建线程对象
Thread thread = new ThreadDemo2();
//2.调用线程的run方法,这种方式调用是主线程调用run方法,并没有创建子线程。
//thread.run();
//3.调用start()方法,一个线程对象只能调用一个start()方法,否则报异常,因为当前线程的状态
//已经变为运行状态
thread.start();
thread.start();
}
}
2.3 线程练习题
练习题:创建两个子线程,让其中一个线程输出1-100之间的偶数,另一个线程输出1-100之间的奇数。
参考答案1:
public class ThreadDemo3 {
public static void main(String[] args) {
Thread t1 = new Thread1();
Thread t2 = new Thread2();
t1.start();
t2.start();
}
}
//1.打印偶数线程
class Thread1 extends Thread{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if(i%2==0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+i);
}
}
}
}
//2.打印奇数线程
class Thread2 extends Thread{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if(i%2!=0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+i);
}
}
}
}
参考答案2:
public class ThreadDemo4 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2==0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2!=0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName()+":"+i);
}
}
}
}.start();
}
}
2.4 jvm创建线程的过程
以上面练习为例,jvm创建线程的过程如下:

2.5 小结
- 如何创建多线程程序
- 多线程程序的执行过程
三、Thread类API
3.1 Thread类构造方法
Thread类的构造方法如下:
- Thread() 分配一个新的 Thread对象。
- Thread(Runnable target) 分配一个新的 Thread对象。
- Thread(Runnable target, String name) 分配一个新的 Thread对象。
- Thread(String name) 分配一个新的 Thread对象。
3.1.1 创建线程并设置线程的名字
示例1:创建线程并设置线程的名字
public class ThreadDemo5 extends Thread {
/*程序入口*/
public static void main(String[] args) {
//创建线程
Thread thread = new ThreadDemo5("线程1");
//启动线程
thread.start();
}
@Override
public void run() {
System.out.println("子线程名字:"+this.getName());
}
public ThreadDemo5(String name) {
super(name);
}
}
3.1.2 通过实现Runnable接口的方式创建线程
我们发现Thread(Runnable target, String name) 这个构造方法需要传入一个Runnable接口,Runnable接口Api如下:

示例1:通过实现Runnable接口创建线程1、线程2,并分别打印1-100的偶数,1-100的奇数
/**
* 通过实现Runnable接口创建线程1、线程2,并分别打印1-100的偶数,1-100的奇数
*/
public class ThreadDemo6 {
public static void main(String[] args) {
//创建线程1
Thread thread1 = new Thread(new Runnable1(),"线程1");
//创建线程2
Thread thread2 = new Thread(new Runnable2(),"线程2");
//启动线程
thread1.start();
thread2.start();
}
}
class Runnable1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if(i%2==0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
class Runnable2 implements Runnable{
@Override
public void run() {
for (int i = 0; i <=100; i++) {
if(i%2!=0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
3.2 Thread 类常用方法
3.2.1 Thread 类基本方法
线程的常用方法如下:
start():启动当前线程;调用当前线程的run()
run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字 或者构造方法
sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前
线程是阻塞状态。
示例1:线程的常用方法演示
/** * 线程的常用方法: * 1. start():启动当前线程;调用当前线程的run() * 2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 * 3. currentThread():静态方法,返回执行当前代码的线程 * 4. getName():获取当前线程的名字 * 5. setName():设置当前线程的名字 或者构造方法 * 6. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前 * 线程是阻塞状态。 */
public class ThreadDemo7 extends Thread{
/*程序入口*/
public static void main(String[] args) throws InterruptedException {
//1.创建线程
Thread thread = new ThreadDemo7();
//2.设置线程名字
thread.setName("子线程1");
//3.开启线程
thread.start();;
//让当前线程,既主线程睡眠5S
Thread.sleep(5000);
//获取当前代码所在线程,既主线程,以及线程名字
System.out.println(Thread.currentThread().getName());
}
@Override
public void run() {
System.out.println("子线程:"+this.getName());
}
}
3.2.1 停止线程
停止线程的stop() 方法已经过时; 一般采标记状态的方式结束线程;示例如下:
示例1:终止线程
/** 终止线程:标记状态 */
public class ThreadDemo8 extends Thread{
public boolean tag = true;
public void setTag() {
this.tag = false;
}
@Override
public void run() {
while(tag){
System.out.println(Thread.currentThread().getName()+":运行中");
}
}
public static void main(String[] args) throws InterruptedException {
//1.开启线程
ThreadDemo8 thread = new ThreadDemo8();
thread.start();
//2.主线程睡5s
Thread.sleep(5000);
//3.终止线程
thread.setTag();
}
}
3.2.2 线程的调度
任务调度策略分为两种:
- 时间片策略调度
- 优先级策略调度
java多线程在处理多线程时,采用的调度策略是:同优先级线程组成先进先出队列(先到先服务),使用时间片策略;对高优先级,使用优先调度的抢占式策略。
java设置优先级的常用方法:
- getPriority() 返回线程优先值、
- setPriority(int newPriority) 改变线程的优先级
优先级的内置属性有:
- MAX_PRIORITY :10
- MIN _PRIORITY :1
- NORM_PRIORITY:5
注意:低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
示例1:设置线程的优先级
/**
* 设置线程的优先级
*/
public class ThreadDemo9 extends Thread {
@Override
public void run() {
System.out.println(this.getName()+"运行");
}
public static void main(String[] args) {
ThreadDemo9 threadDemo1 = new ThreadDemo9();
threadDemo1.setName("线程1");
threadDemo1.setPriority(1);
ThreadDemo9 threadDemo2 = new ThreadDemo9();
threadDemo2.setName("线程2");
threadDemo2.setPriority(5);
ThreadDemo9 threadDemo3 = new ThreadDemo9();
threadDemo3.setName("线程3");
threadDemo3.setPriority(10);
threadDemo1.start();
threadDemo2.start();
threadDemo3.start();
}
}
3.2.3 守护线程
守护线程是区别于用户线程,用户线程即我们手动创建的线程,而守护线程是程序运行的时候在后台提供一种通用服务的线程。垃圾回收线程就是典型的守护线程。
用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
示例1:设置守护线程
/** * 守护线程演示 */
public class DaemonThreadDemo {
public static void main(String[] args) throws InterruptedException {
//创建线程
DaemonThread daemonThread = new DaemonThread();
//设置位守护线程,守护jvm中任何一个非守护线程执行结束
daemonThread.setDaemon(true);
daemonThread.start();
//主线程3s后结束
Thread.sleep(3000);
System.out.println("主线程执行结束");
}
}
class DaemonThread extends Thread{
@Override
public void run() {
try {
System.out.println("守护程执行开始");
Thread.sleep(5000);
System.out.println("守护程执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.3 小结
- 线程两种创建方式
- 线程的常用方法
- 停止线程
- 线程调度
- 守护线程
四、线程的生命周期
4.1 线程的生命周期介绍
操作系统层面线程的生命周期可以用五态模型来描述。分别是初始状态、就绪状态、运行状态、阻塞状态、终止状态。状态之间的流转如下图

Java层面线程明确定义了六种状态,分别是NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,源码如下
/** * A thread state. A thread can be in one of the following states: * <ul> * <li>{@link #NEW}<br> * A thread that has not yet started is in this state. * </li> * <li>{@link #RUNNABLE}<br> * A thread executing in the Java virtual machine is in this state. * </li> * <li>{@link #BLOCKED}<br> * A thread that is blocked waiting for a monitor lock * is in this state. * </li> * <li>{@link #WAITING}<br> * A thread that is waiting indefinitely for another thread to * perform a particular action is in this state. * </li> * <li>{@link #TIMED_WAITING}<br> * A thread that is waiting for another thread to perform an action * for up to a specified waiting time is in this state. * </li> * <li>{@link #TERMINATED}<br> * A thread that has exited is in this state. * </li> * </ul> * * <p> * A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect * any operating system thread states. * * @since 1.5 * @see #getState */
public enum State {
/** * Thread state for a thread which has not yet started. */
NEW,
/** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */
RUNNABLE,
/** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */
BLOCKED,
/** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */
WAITING,
/** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */
TIMED_WAITING,
/** * Thread state for a terminated thread. * The thread has completed execution. */
TERMINATED;
}
4.2 线程的生命周期演示
4.2.1 new、runnable、terminated状态演示
示例1:新建、运行、死亡状态演示如下
/** * 演示生命周期的:新建、运行、死亡状态演示 */
public class ThreadLiftCycleDemo1 extends Thread {
@Override
public void run() {
for (int i = 1; i <=100; i++) {
System.out.println(i);
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程对象
ThreadLiftCycleDemo1 threadLiftCycleDemo1 = new ThreadLiftCycleDemo1();
//查看线程状态:新建状态
System.out.println(threadLiftCycleDemo1.getState());
//启动线程
threadLiftCycleDemo1.start();
//查看线程状态:运行状态
System.out.println(threadLiftCycleDemo1.getState());
//让主线程睡眠1s,等待子线程执行结束
Thread.sleep(1000);
//查看线程状态:死亡状态
System.out.println(threadLiftCycleDemo1.getState());
}
}
4.2.2 timed_waiting 状态演示
示例2:计时等待状态
/** * 演示生命周期的:计时等待状态 */
public class ThreadLiftCycleDemo2 extends Thread{
@Override
public void run() {
System.out.println("子线程");
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程对象
ThreadLiftCycleDemo2 threadLiftCycleDemo2 = new ThreadLiftCycleDemo2();
//开启线程
threadLiftCycleDemo2.start();
//让主线程睡眠0.5s,等待子线程进入沉睡状态,并查看
Thread.sleep(500);
System.out.println(threadLiftCycleDemo2.getState());
}
}
4.2.3 waiting 状态演示
示例3:等待状态演示
需求:用两个线程模仿动车和绿皮车经过一个交叉口,绿车先到,但需要等待动车先过交叉口后在经过交叉口。
/** * 示例3:等待状态演示 * * 需求:用两个线程模仿动车和绿皮车经过一个交叉口,绿车先到,但需要等待动车先过交叉口后在经过交叉口。 */
public class ThreadLiftCycleDemo3 {
/*程序入口*/
public static void main(String[] args) throws InterruptedException {
//创建动车
BulletTrain bulletTrain = new BulletTrain();
//创建慢车
GreenTrain greenTrain = new GreenTrain(bulletTrain);
//开启线程
bulletTrain.start();
greenTrain.start();
//主线程睡眠1.5s后查看慢车线程状态
Thread.sleep(1500);
System.out.println(greenTrain.getState());
}
}
//绿皮车线程
class GreenTrain extends Thread{
private BulletTrain bulletTrain;
public GreenTrain(BulletTrain bulletTrain){
this.bulletTrain = bulletTrain;
}
@Override
public void run() {
try {
System.out.println("绿车还有1s到交叉路口");
Thread.sleep(1000);
//当前线程等待bulletTrain线程先执行完在执行
bulletTrain.join();
System.out.println("绿车通过");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//动车线程
class BulletTrain extends Thread{
@Override
public void run() {
try {
System.out.println("动车还有2s到交叉路口");
Thread.sleep(2000);
System.out.println("动车通过");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
阻塞状态在后面的章节中演示
4.3 小结
- 操作系统的线程生命周期分为5各状态:新建、就绪、运行、阻塞、结束
- java对线程的状态进行更细微的划分:新建、运行、等待、计时等待、阻塞、结束
- join方法 当前线程等待调用join方法所属的线程运行完在运行。
五、总结
- 进程、线程
- 线程的两种创建方式
- 线程的常用方法
- 线程停止
- 线程的调度
- 守护线程
- 线程的生命周期
边栏推荐
- 虹科Automation softPLC | 虹科KPA MoDK运行环境与搭建步骤(3)——MoDK例程测试
- Leetcode 557. reverse word III in string
- 基于udp通信的在线多人聊天室
- Webshell管理工具的流量特征
- 使用STP生成树协议解决网络中的二层环路问题
- day17_集合下
- Leetcode notes 452. minimum number of arrows to burst balloons (medium) 452. detonate balloons with the minimum number of arrows (medium)
- [leetcode skimming] array 1 - double pointer
- Arrays&Object&System&Math&Random&包装类
- c语言问题
猜你喜欢

Vivado IP核之复数浮点数乘法 Floating-point

FIR滤波器设计(1)——利用matlab的fdatool工具箱设计FIR滤波器参数
![[leetcode skimming] array 2 - binary search](/img/50/c006cbe5a91774c99eb782d9203fa0.png)
[leetcode skimming] array 2 - binary search

虹科分享 | 如何测试与验证复杂的FPGA设计(1)——面向实体或块的仿真

官方教程 Redshift 02 4中GI引擎概述及总结

2022 summer second day information competition learning achievement sharing 2

FTP的两种模式详解

Leetcode 557. reverse word III in string
![[leetcode skimming] array 1 - double pointer](/img/c3/a671395e20fad58f1c7f6abc6e1a39.png)
[leetcode skimming] array 1 - double pointer

子网数、主机数与子网掩码的关系
随机推荐
Leetcode 19. delete the penultimate node of the linked list
UE4 天光和反射球的原理和区别
V-ray 5 ACEScg 工作流程设置
Access、Hybrid和Trunk三种模式的理解
What are the advantages of software testing? See how much you know
THINKPHP5 常见问题
Learning notes of bit operation
服务器135、137、138、139、445等端口解释和关闭方法
Official tutorial redshift 05 AOVs
运算符重载
Arrays&Object&System&Math&Random&包装类
9196 tumor area solution
盘点 | 全球关键信息基础设施网络安全大事件
Unity初学4——帧动画以及主角攻击(2d)
Sliding window leetcode 76. minimum covering substring (hard) 76.76. minimumwindow substring (hard)
官方教程 Redshift 05 AOVs
Ue5 landscape conversion Nanite conversion method and it does not support the use method of starting dynamic mesh with lumen and lumen
Official tutorial redshift 07 instances and proxy
On defect description style
赛博朋克版特效shader