Java ---多线程

news2025/1/13 10:04:43

(一)目录

 多线程介绍
线程的创建
线程的使用
线程的优先级
守护线程
线程同步
线程并发协作

(二)多线程介绍 

1.多线程的基本概念

程序
   “程序(Program)”是一个静态的概念,一般对应于操作系统中的一个可执行文件,比 如:我们要启动网易云听音乐,则需要执行网易云对应的的可执行程序。当我们双击网易云的可执行程序后操作系统会将该程序加载到内存中,开始执行该程序,于是产生了“进程”。
进程
      
执行中的程序叫做进程(Process),是一个动态的概念。其实进程就是一个在内存中独
立运行的程序空间 。如正在运行的写字板程序就是一个进程。
   
  •  进程是程序的一次动态执行过程, 占用特定的地址空间。

  •  每个进程由 3 部分组成:cpu、data、code。每个进程都是独立的,保有自 己的 cpu 时间,代码和数据,即便用同一份程序产生好几个进程,它们之间还是拥有自己的这 3 样东西,这样的缺点是:浪费内存,cpu 的负担较重。

  • 多任务(Multitasking)操作系统将 CPU 时间动态地划分给每个进程,操作系统同时执行多个进程,每个进程独立运行。以进程的观点来看,它会以为自己独占 CPU 的使用权。
  • 进程的查看 :Windows 系统: Ctrl+Alt+Del,启动任务管理器即可查看所有进程。 

线程

    一个进程可以产生多个线程。同多个进程可以共享操作系统的某些资源一样,同一进程

的多个线程也可以共享此进程的某些资源(比如:代码、数据),所以线程又被称为轻量级
进程(lightweight process)。
  •  一个进程内部的一个执行单元,它是程序中的一个单一的顺序控制流程。

  •  一个进程可拥有多个并行的(concurrent)线程。

  • 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从同一堆中分配对象并进行通信、数据交换和同步操作。

  • 由于线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制, 这就使得通信更简便而且信息传递的速度也更快。
  • 线程的启动、中断、消亡,消耗的资源非常少。

2.线程和进程的区别

  •  线程在进程中运行的。

  •  一个进程可以包含多个线程。

  • 不同进程间数据很难共享,而同一进程下不同线程间数据很易共享。

  •  进程要比线程消耗更多的计算机资源。

  • 进程间不会相互影响,因为它们的空间是完全隔离的。而进程中的一个线程挂掉将导致整个进程挂掉。

  • 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
  • 一个进程如果只有一个线程则可以被看作单线程的,如果一个进程内拥有多个线程,进程的执行过程不是一条线(线程)的,而是多条线(线程)共同完成的。

并发

      并发是指在一段时间内同时做多个事情。当有多个线程在运行时,如果只有一个 CPU,这种情况下计算机操作系统会采用并发技术实现并发运行,具体做法是采用“ 时间片轮询算法”,在一个时间段的线程代码运行时,其它线程处于就绪状。这种方式我们称之为并发(Concurrent)。

4 线程的执行特点

4.1 方法的执行特点

 4.2线程的执行特点

5  主线程以及子线程

5.1 主线程
        当 Java 程序启动时,一个线程会立刻运行,该线程通常叫做程序的主线程( main thread ), 即 main 方法对应的线程,它是程序开始时就执行的。
       Java 应用程序会有一个 main 方法,是作为某个类的方法出现的。当程序启动时,该方法就会第一个自动的得到执行,并成为程序的主线程。也就是说,main 方法是一个应用的入口,也代表了这个应用的主线程。 JVM 在执行 main 方法时 ,main 方法会进入到栈内存 ,JVM会通过操作系统开辟一条 main 方法通向 cpu 的执行路径 ,cpu 就可以通过这个路径来执行main 方法 , 而这个路径有一个名字 , main( ) 线程。
主线程的特点:
    它是产生其他子线程的线程。它不一定是最后完成执行的线程,子线程可能在它结束之后还在运行。
5.2 子线程
    在主线程中创建并启动的线程,一般称之为子线程。

(三)创建线程

在 Java 中使用多线程非常简单,我们先学习如何创建线程,然后再结合案例深入剖析
线程的特性。
1 通过继承 Thread 类实现多线程
在 Java 中负责实现线程功能的类是 java.lang.Thread 类。
继承 Thread 类实现多线程的步骤:
①继承 Thread 类定义线程类。
②重写 Thread 类中的 run( )方法。run( )方法也称为线程体。
③ 实例化线程类并通过 start()方法启动线程。
 
public class TestThread extends Thread {
public TestThread(){
System.out.println(this.getName());
}
/**
* 线程的线程体
*/
@Override
public void run() {
System.out.println(this.getName()+"线程开始");
for(int i=0;i<20;i++){
System.out.println(this.getName()+" "+i);
7
}
System.out.println(this.getName()+"线程结束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
TestThread t1 = new TestThread();
//启动线程
t1.start();
TestThread t2 = new TestThread();
//启动线程
t2.start();
System.out.println("主线程结束");
}
}
2 通过实现 Runnable 接口实现多线程
public class TestThread2 implements Runnable {
public TestThread2(){
System.out.println(Thread.currentThread().getName());
}
/**
* 当前线程的线程体方法
*/
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开
始");
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+"
"+i);
}
8
System.out.println(Thread.currentThread().getName()+" 线程结
束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
TestThread2 testThread2 = new TestThread2();
Thread t1 = new Thread(testThread2);
t1.start();
Thread t2 = new Thread(new TestThread2());
t2.start();
System.out.println("主线程结束");
}
}
3 线程的执行流程
 

 4 线程的生命周期

 简言之,一个线程对象在它的生命周期内,需要经历 5 个状态。(新生、就绪、运行、堵塞、死亡)。

   ⑴新生状态(New)
      用 new 关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用 start 方法进入就绪状态。
  ⑵ 就绪状态(Runnable)
      处于就绪状态的线程已经具备了运行条件,但是还没有被分配到 CPU,处于“线程就 绪队列”,等待系统为其分配 CPU。就绪状态并不是执行状态,当系统选定一个等待执行的 Thread 对象后,它就会进入执行状态。一旦获得 CPU,线程就进入运行状态并自动调 用自己的 run 方法。有 4 中原因会导致线程进入就绪状态:
① 新建线程:调用 start()方法,进入就绪状态;
② 阻塞线程:阻塞解除,进入就绪状态;
③ 运行线程:调用 yield()方法,直接进入就绪状态;
 ④ 运行线程:JVM 将 CPU 资源从本线程切换到其他线程。
⑶ 运行状态(Running)
     在运行状态的线程执行自己 run 方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。
阻塞状态(Blocked)
        阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。有 4 种原因会导致阻塞:
① 执行 sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
② 执行 wait()方法,使当前线程进入阻塞状态。当使用 nofity()方法唤醒这个线程后,它进入就绪状态。
③ 线程运行时,某个操作进入阻塞状态,比如执行 IO 流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
④  join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用 join()方法。
死亡状态(Terminated)
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行 的线程完成了它 run()方法内的全部工作; 另一个是线程被强制终止,如通过执行 stop() 或 destroy()方法来终止一个线程(注:stop()/destroy()方法已经被 JDK 废弃,不推荐使
用)。
   注:当一个线程进入死亡状态以后,就不能再回到其它状态了。

(三) 线程的使用

1 终止线程

      如果我们想在一个线程中终止另一个线程我们一般不使用 JDK 提供的 stop()/destroy()
方法(它们本身也被 JDK 废弃了)。通常的做法是提供一个 boolean 型的终止变量,当这个
变量值为 false 时,则终止线程的运行。
public class StopThread implements Runnable {
private boolean flag = true;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开
始");
int i= 0;
while(flag){
System.out.println(Thread.currentThread().getName()+" "+i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 线程结
束");
12
}
public void stop(){
this.flag = false;
}
public static void main(String[] args)throws Exception {
System.out.println("主线程开始");
StopThread st = new StopThread();
Thread t1 = new Thread(st);
t1.start();
System.in.read();
st.stop();
System.out.println("主线程结束");
}
}

2 暂停当前线程执行 sleep/yield

暂停线程执行常用的方法有 sleep()和 yield()方法,这两个方法的区别是:
   sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状
态。
     yield()方法:可以让正在运行的线程直接进入就绪状态,让出 CPU 的使用权。
 

2.2yield 方法的使用
  yield() 方法的作用:暂停当前正在执行的线程,并执行其他线程。
yield() 让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得
运行的机会。因此,使用 yield() 的目的是让具有相同优先级的线程之间能够适当的轮换执行。
但是,实际中无法保证 yield() 达到让步的目的,因为,让步的线程可能被线程调度程序再次
选中。
使用 yield 方法时要注意的几点:
  •  yield 是一个静态的方法。

  • 调用 yield 后,yield 告诉当前线程把运行机会交给具有相同优先级的线程。

  • yield 不能保证,当前线程迅速从运行状态切换到就绪状态。

  •  yield 只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。

 

3    线程的联合

     当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不
能再次执行。线程 A 在运行期间,可以调用线程 B 的 join()方法,让线程 B 和线程 A 联合。
这样,线程 A 就必须等待线程 B 执行完毕后,才能继续执行。
3.1join 方法的使用
join() 方法就是指调用该方法的线程在执行完 run() 方法后,再执行 join 方法后面的代码,
即将两个线程合并,用于实现同步控制

 

3.2 线程联合案例
/**
* 儿子买烟线程
*/
class SonThread implements Runnable{
@Override
public void run() {
System.out.println("儿子出门买烟");
System.out.println("儿子买烟需要 10 分钟");
for(int i=0;i<10;i++){
System.out.println("第"+i+"分钟");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("儿子买烟回来了");
}
}
/**
* 爸爸抽烟线程
*/
17
class FatherThread implements Runnable{
@Override
public void run() {
System.out.println("爸爸想抽烟,发现烟抽完了");
System.out.println("爸爸让儿子去买一包红塔山");
Thread t = new Thread(new SonThread());
t.start();
System.out.println("等待儿子买烟回来");
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("爸爸出门找儿子");
System.exit(1);
}
System.out.println("爸爸高兴的接过烟,并把零钱给了儿子");
}
}
public class JoinDemo {
public static void main(String[] args) {
System.out.println("爸爸和儿子买烟的故事");
Thread t = new Thread(new FatherThread());
t.start();
}
}

4 Thread 类中的其他常用方法

4.1 获取当前线程名称
4.1.1 方式一:this.getName() 获取线程名称
this.getName() 获取线程名称,该方法适用于继承 Thread 实现多线程方式。
class GetName1 extends Thread{
@Override
public void run() {
System. out .println( this .getName());
}
}
4.1.2 方式二:Thread.currentThread().getName() 获取线程名称
Thread.currentThread().getName() 获取线程名称,该方法适用于实现 Runnable 接口实现多线程方式。
class GetName2 implements Runnable{
@Override
public void run() {
System. out .println(Thread. currentThread ().getName());
}
}

 

4.2 设置线程的名称
4.2.1 方式一:通过构造方法设置线程名称。
class SetName1 extends Thread{
public SetName1(String name){
super (name);
}
@Override
public void run() {
System. out .println( this .getName());
}
}
public class SetNameThread {
public static void main(String[] args) {
SetName1 setName1 = new SetName1( "SetName1" );
setName1.start();
}
}

 

4.2.2 方式二通过 setName() 方法设置线程名称。
class SetName2 implements Runnable{
@Override
public void run() {
System. out .println(Thread. currentThread ().getName());
}
}
public class SetNameThread {
public static void main(String[] args) {
/*SetName1 setName1 = new SetName1();
setName1.setName("SetName1");
setName1.start();*/
Thread thread = new Thread( new SetName2());
thread.setName( "SetName2" );
thread.start();
}
}

 

4.3 判断当前线程是否存活
  isAlive() 方法: 判断当前的线程是否处于活动状态。
活动状态是指线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就
认为线程是存活的。
class Alive implements Runnable{
@Override
public void run() {
System. out .println(Thread. currentThread ().isAlive()+ " 2" );
try {
Thread. sleep ( 20000 );
} catch (InterruptedException e) {
20 e.printStackTrace();
}
}
}
public class AliveThread {
public static void main(String[] args) {
Thread thread = new Thread( new Alive());
System. out .println(thread.isAlive()+ " 1" );
thread.start();
System. out .println(thread.isAlive()+ " 3" );
try {
Thread. sleep ( 1000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
System. out .println(thread.isAlive()+ " 4" );
}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/501748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openpnp - Smoothieware project debug

文章目录 openpnp - Smoothieware project debug概述笔记记录一下工程中, 和调试相关的资料备注END openpnp - Smoothieware project debug 概述 迁出了Smoothieware工程, 想单步调试学习一下工程. 工程可以编译下载, 但是没有对单步调试方法做说明. 研究了几天, 大概知道咋回…

Spring/SpringBoot常用注解总结

为什么要写这篇文章&#xff1f; 最近看到网上有一篇关于 SpringBoot 常用注解的文章被转载的比较多&#xff0c;我看了文章内容之后属实觉得质量有点低&#xff0c;并且有点会误导没有太多实际使用经验的人&#xff08;这些人又占据了大多数&#xff09;。所以&#xff0c;自…

第三章 图像的低通与高通滤波

文章目录 前言一、卷积操作二、低通滤波1. 方盒滤波与均值滤波2. 中值滤波3.高斯滤波3.1 高斯分布3.2 滤波流程3.2 OpenCV代码及手动实现 4. 双边滤波4.1 原理4.2 OpenCV代码实现 二、高通滤波1. Sobel算子2. Schar算子3.拉普拉斯算子4. Canny边缘检测4.1 算法流程4.1.2 图像降…

垃圾回收小程序开发功能与优势有哪些?

垃圾回收、二手资源回收、废旧物品回收是最近几年社会提倡也越来越受到人们重视的&#xff0c;所以市面上也产生了一系列二手资源回收软件助力再生资源得到更好的利用&#xff0c;同时对环境保护也有很积极的作用。 垃圾资源回收小程序借助微信平台强大的用户群体&#…

CCF-202209-2-何以包邮?01背包

目录 1、题目描述&#xff1a; 2、思路1&#xff1a;动态规划 2.1、确定dp数组及下标含义 2.2、递推公式 2.3、初始化dp数组 2.4、确定遍历顺序 2.5、C实现如下 3、思路2&#xff1a;暴力法-空间换时间 1、题目描述&#xff1a; 新学期伊始&#xff0c;适逢顿顿书城有…

OpenGL(八)——图像逆透视算法IPM

目录 一、前言 二、相机模型 2.1 针孔模型 2.2 相机外参 三、逆透视模型 四、算法总结 一、前言 透视变换&#xff08;Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件&#xff0c;按透视旋转定律使承影面&#xff08;透视面&#xff09;绕迹线…

Cesium 实战-解决 The browser supports WebGL, but initialization failed 问题

Cesium 实战-解决 The browser supports WebGL, but initialization failed 问题 系统环境版本试错过程解决问题 在公司内网服务器部署 Cesium 项目的时候&#xff0c;发现提示浏览器不支持 WebGL 错误&#xff0c;经尝试&#xff0c;确认 Cesium 1.101.0 以及之前的版本是可以…

职场小白如何快速成为房地产策划专家?全覆盖解密

如果你是刚入行的地产策划新手小白&#xff1a; 1、首先要会房地产的基础知识&#xff0c;相关的政策法规等&#xff0c;因为这些都是制定策略的最基本的依据&#xff0c;如果这些你都不熟&#xff0c;制定出来的策划就会有错误。建议你多买几本地产基础知识的书看看。 2、把…

14.网络编程基础

1.网络编程入门 1.1 网络编程概述【理解】 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计…

Axios概述

一、Json-server 获得零编码的完整伪造 REST API zero coding 在不到 30 秒的时间内 &#xff08;认真&#xff09;。 使用 <3 创建&#xff0c;适用于需要快速后端进行原型设计和模拟的前端开发人员&#xff0c;模拟后端发送过来json数据。 1.安装 npm install -g jso…

OPNET Modeler 例程——停等协议的建模和仿真

文章目录 一、概述二、链路模型和包格式创建三、进程模型1.src 进程模型2.sink 进程模型 四、节点模型五、网络模型六、仿真结果 一、概述 本例程是在 OPNET Modeler 中对停等协议的建模和仿真&#xff0c;其中停等协议的操作过程如下&#xff1a; &#xff08;1&#xff09;发…

【Spring Cloud Alibaba】Nacos config的使用和高阶用法

文章目录 &#x1f40d;第一步&#xff0c;创建配置文件到nacos中&#x1f40d;第二步&#xff0c;在项目中配置nacos的地址和指定文件&#x1f40d;第三步&#xff0c;读取配置文件&#x1f426;高阶用法&#x1f426;高阶用法一&#xff1a;使用yaml文件&#x1f426;第一步&…

5年功能测试,薪资定格8K迷茫了....我该如何破局?

前言 来自一位粉丝的投稿&#xff0c;从毕业开始就一直在从事软件测试的工作&#xff0c;到目前已经是第5个年头了&#xff0c;从4k涨到了8K&#xff0c;显而易见我们这位粉丝并不满足现状&#xff0c;于是问我怎么破局&#xff0c;他当下应该干什么事情,或者应该学习什么技术…

从零开始学习Linux运维,成为IT领域翘楚(十)

文章目录 &#x1f525;Linux网络防火墙&#x1f525;Linux内核机制 &#x1f525;Linux网络防火墙 防火墙管理工具 firewalld概述 Centos 系统中集成了多款防火墙管理工具&#xff0c;其中 firewalld服务是默认的防火墙配置管理工具&#xff0c;它拥有基于 CLI&#xff08;…

Aha! Adaptive History-driven Attack for Decision-based Black-box Models

AHA!基于决策的黑盒模型的自适应历史驱动攻击 Aha! Adaptive History-driven Attack for Decision-based Black-box Models ABSTRACT 基于决策的黑盒攻击指的是只使用受害者模型的前1个标签来制作对抗示例。一种常见的做法是从一个大的扰动开始&#xff0c;然后用一个确定的方…

【Nacos源码分析】

Nacos源码分析 Nacos源码分析1.下载Nacos源码并运行1.1.下载Nacos源码1.2.导入Demo工程1.3.导入Nacos源码1.4.proto编译1.4.1.什么是protobuf1.4.2.安装protoc1.4.3.编译proto 1.5.运行 2.服务注册2.1.服务注册接口2.2.客户端2.2.1.NacosServiceRegistryAutoConfiguration2.2.2…

【软件测试】| 软件测试 - 答疑篇

&#x1f397;️ 主页&#xff1a;小夜时雨 &#x1f397;️ 专栏&#xff1a;软件测试 &#x1f397;️ 如何优雅的活着&#xff0c;是我找寻的方向 目录 一、什么是软件测试二、测试和调试的区别三、软件测试和开发的区别 一、什么是软件测试 最常见的理解是&#xff1a;软…

使用J-Link的J-Scope功能查看数据实时波形

使用串口打印波形的不便之处 对于要查看的实时变量&#xff0c;一般可以用串口打印到可以查看波形的上位机上。但是这种办法有几个不方便的地方&#xff1a; 需要根据配套上位机的通讯协议&#xff0c;在单片机上编写上传数据的代码 单片机CPU需要浪费部分时间在串口数据上传上…

Makefile基础教程(变量的介绍和使用)

文章目录 前言一、Makefile变量概念介绍二、Makefile中变量的赋值方式1.简单赋值2.递归赋值3.条件赋值4.追加赋值 三、Makefile赋值在工程中的应用总结 前言 在C语言等语言中存在变量这个概念那么在Makefile中也是存在变量这个概念的&#xff0c;现在就让我们来学习一下什么是…

Solr(5):Solr控制台说明-主面板

1 Dashboard(仪表盘) 访问 http://ip:8983/solr时&#xff0c;出现该主页面&#xff0c;可查看到solr运行时间、solr版本&#xff0c;系统内存、虚拟机内存的使用情况 这里的图片描述 2 Logging(日志) 显示solr运行出现的异常或错误 3 Core Admin (core管理) 主要有Add Cor…