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

【Java】多线程篇

多线程的概念

线程:是操作系统能够进行运输调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。可以简单理解为,应用软件中互相独立,可以同时运行的功能

进程:是程序的基本执行实体

多线程:可以将线程简单理解为,应用软件中互相独立,可以同时运行的功能。而多线程就是同时运行多个这样的功能,可以让程序同时做多件事

多线程作用:提高效率(压榨 CPU)

多线程应用场景

  • 服务端领域

    • Web 服务:处理大量并发请求,像电商网站高峰期多用户同时访问,多线程让服务器并行响应,提升并发处理力和响应速度。
    • 数据库服务:多个客户端同时进行读写操作时,多线程并行处理,提高数据库吞吐量和性能。
  • 界面相关应用

    • 桌面软件:进行图形渲染、文件读写等耗时操作时,多线程避免界面卡顿,保证流畅交互。

    • 游戏开发:不同线程分别负责逻辑计算、图形渲染、网络通信等,实现流畅游戏体验。

  • 数据计算场景

    • 科学计算:气象预报、基因测序等需大量数值计算,多线程分解任务并行处理,缩短计算时间。

    • 大数据处理:对海量数据进行挖掘、分析,多线程同时处理不同部分数据,加快处理进程。

  • 异步 I/O 操作

    • 文件读写:读写大文件时,多线程让读写与其他任务并行,提高整体效率。

    • 网络通信:即时通讯等应用中,不同线程分别负责收、发消息,保证消息及时处理。

并发:在同一时刻,有多条指令在单个 CPU 上交替执行

并行:在同一时刻,有多条指令在多个 CPU(多核) 上同时执行

多线程的实现方式

继承 Thread 类的方式进行实现

操作步骤

  1. 创建一个子类继承 Thread 类
  2. 重写 Thread 类中的 run()
  3. 创建子类对象并启动线程

代码示例

public class MyThread extends Thread{
   
	
	@Override
	public void run() {
   
		for (int i = 0; i < 100; i++) {
   
			System.out.println(getName() + "helloworld");
		}
	}
	
	public static void main(String[] args) {
   
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		
		mt1.setName("线程1:");
		mt2.setName("线程2:");
		
		// 开启线程
		mt1.start();
		mt2.start();
	}
}

实现 Runnable 接口的方式进行实现

操作步骤

  1. 创建一个类实现 Runnable 接口
  2. 重写 Runnable 接口中的 run()
  3. 创建实现类对象
  4. 创建一个 Thread 类对象并启动线程

代码示例

public class MyRun implements Runnable{
   

	@Override
	public void run() {
   
		for (int i = 0; i < 100; i++) {
   
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "HelloWorld");
			//System.out.println(Thread.currentThread().getName() + "HelloWorld");
		}
		
	}

	public static void main(String[] args) {
   
		
		MyRun mr = new MyRun();
		
		Thread t1 = new Thread(mr);
		Thread t2 = new Thread(mr);
		
		t1.setName("线程1:");
		t2.setName("线程2:");
		
		t1.start();
		t2.start();
	}
}

利用 Callable 接口和 FutureTask 类

特点:可以获取到多线程运行的结果

操作步骤

  1. 创建一个类实现 Callable 接口
  2. 重写 call()(是有返回值的,表示多线程运行的结果)
  3. 创建实现类对象(表示多线程要执行的任务)
  4. 创建 FutureTask 类的对象(作用:管理多线程运行的结果)
  5. 创建 Thread 类的对象并启动线程

代码示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable<Integer>{
   

	@Override
	public Integer call() throws Exception {
   
		int sum = 0;
		for(int i = 1;i <= 100;i++) {
   
			sum = sum + i;
		}
		return sum;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
   
		
		MyCallable mc = new MyCallable();
		
		FutureTask<Integer> ft = new FutureTask<Integer>(mc);
		
		Thread t1 = new Thread(ft);
		
		t1.start();
		
		Integer result = ft.get();
		System.out.println(result);
	}

}

三种方式对比

对比维度 继承 Thread 实现 Runnable 接口 实现 Callable 接口并结合 FutureTask
代码复杂度 简单直观,只需继承 Thread 类并重写 run() 方法,但受单继承限制 稍高,需额外创建 Thread 对象并传入 Runnable 实例,可避免单继承限制 最高,要创建 Callable 类、FutureTask 包装对象和 Thread 对象
线程执行结果获取 无法直接获取,run() 方法返回值为 void,需借助共享变量、回调函数等 无法直接获取,run() 方法返回值为 void,需借助额外手段 可直接获取,通过 FutureTaskget() 方法阻塞等待获取 call() 方法返回值
资源共享和线程安全性 不同线程实例有独立实例变量,共享需用静态变量,需额外同步机制保证安全 多个线程可共享同一 Runnable 实例成员变量,需同步机制保证数据一致性 Runnable 类似,多个线程可共享同一 Callable 实例成员变量,需同步机制
扩展性和灵活性 受单继承限制,扩展性差 不影响类继承关系,扩展性和灵活性好,可分离任务和线程创建 不影响类继承关系,扩展性和灵活性好,可分离任务和线程创建

常见的成员方法

线程的命名和休眠

  • String getName():方法名,返回此线程的名称,返回值类型为String
  • void setName(String name):方法名,用于设置线程的名字(构造方法也可设置),无返回值(void) 。

代码示例

public class MyThread extends Thread{
   
	
	public MyThread() {
   }
	public MyThread(String name) {
   
		super(name);
	}
	
	@Override
	public void run() {
   
		for (int i = 1; i <= 3; i++) {
   
			System.out.println(getName() + "@" + i);
		}
	}
	
	public static void main(String[] args) {
   
		// 构造方法命名
		MyThread mt1 = new MyThread("线程1");
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		// setName()命名
		mt2.setName("线程2");

		mt1.start();
		mt2.start();
		// 默认命名
		mt3.start();
	}
}

程序运行结果如下:

线程1@1
线程1@2
Thread-1@1
Thread-1@2
Thread-1@3
线程2@1
线程2@2
线程2@3
线程1@3

注意事项

  1. 如果没有给线程命名,线程会有默认名,格式:Thread-X(X 是序号,从 0 开始)
  2. 线程命名可以使用 setName(),也可使用构造方法,由于子类不会继承父类的构造方法,所以不要忘了在继承类中写构造方法
  • static Thread currentThread():静态方法,获取当前线程的对象,返回值类型为Thread

在前面实现 Runnable 接口中使用过该方法,就不写代码示例了,不过需要思考一个问题,在 main 方法中不开启线程,用 currentThread 方法获取的对象是什么?

public class MyThread extends Thread{
   
	
	@Override
	public void run() {
   
		for (int i = 1; i <= 3; i++) {
   
			System.out.println(getName() + "@" + i);
		}
	}
	
	public static void main(String[] args) {
   
		
		Thread t = Thread.currentThread();
		System.out.println(t.getName()); // 输出 main
        
	}
}

当 JVM 虚拟机启动之后,会自动开启多条线程,其中有一条就是 main 线程,作用是调用 main 方法并执行其中的代码

  • static void sleep(long time):静态方法,让线程休眠指定的毫秒数,无返回值(void)。

代码示例

public class Test {
   

	public static void main(String[] args) throws InterruptedException {
   
		System.out.println("123");
		long start = System.currentTimeMillis();
		Thread.sleep(5000);
		long end = System.currentTimeMillis();
		long time = (end - start) / 1000;
		System.out.println("线程休眠了"+ time + "秒");
		System.out.println("456");
	}

}

注意事项

  1. 执行到这个方法的线程会在这里停留对应的时间
  2. 当时间到了后,线程会自动继续执行后面的代码

线程的优先级

调度分为抢占式

相关文章:

  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_fd_info
  • redis slaveof 命令 执行后为什么需要清库重新同步
  • 阿里云oss文件上传springboot若依java
  • 用C语言实现一个链表(四)
  • 深度学习原理与Pytorch实战
  • VS Code 如何搭建CC++开发环境
  • 【C语言】字符函数与字符串函数
  • 【动手实验】TCP半连接队列、全连接队列实战分析
  • 军事理论综合版参考答案
  • 计算机三级网络技术备考(3)
  • 大白话React 虚拟 DOM,好处在哪里?跟vue有什区别
  • Linux上用C++和GCC开发程序实现两个不同MySQL实例下单个Schema稳定高效的数据迁移到其它MySQL实例
  • Linux 检测内存泄漏方法总结
  • linux中断调用流程(arm)
  • getMergedLocalBeanDefinition 方法的作用
  • ChatGPT与DeepSeek:AI语言模型的巅峰对决
  • 广东专插本-政治毛泽东思想学习笔记
  • 深入浅出数据结构(图)
  • 使用Hydra进行AI项目的动态配置管理
  • 【大模型技术】大模型推理优化方法及代码实现总结篇
  • 网站建设工具品牌/北京cms建站模板
  • 编程自学免费网站/网络营销岗位描述的内容
  • 南京做网站费用/网络营销的特点有哪些
  • 创建一个网站需要多少钱/专业关键词优化平台
  • 淡水网络公司做网站/数字化营销怎么做
  • 娄底网站建设79ld/搜索量查询百度指数