【Java基础】 多线程

Java 多线程编程是指在一个 Java 应用程序中同时运行多个线程。线程是一个程序执行的最小单位,它包含在进程中,利用多线程可以提高应用程序的性能和响应能力。多线程编程在 Java 中是一个重要的概念,尤其是在处理并发任务时。


 
一、线程的概念

程序、进程、多任务和线程等是非常容易混淆的概念。为了更好地理解多线程机制,有必要搞清楚这些概念。

程序(Program)

程序是含有指令和数据的文件,被储存在磁盘或其他的数据储存设备中,也就是说程序是静态的代码。

进程(Process)

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建、运行到消亡的全过程。简单的说,一个进程就是一个执行中的程序。

多任务(Multi task)

多任务是指在一个系统中可以同时运行多个进程,即有多个独立运行的任务,每一个任务对应一个进程。每个进程都有一段专用的内存区域,即使是多次启动同一段程序产生不同的进程也是如此。

线程(Thread)

线程是操作系统中进行调度和执行的最小单位,它包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。所谓多线程就是同时执行一个以上的线程,一个线程的执行不必等待另一个线程执行完后才执行,所有线程都可以发生在同一时刻。但操作系统并没有将多个线程看作多个独立的应用去实现线程的调度和管理以及资源分配。

注意:多任务与多线程是两个不同的概念,多任务是针对操作系统而言的,表示操作系统可以同时运行多个应用程序,而多线程是指一个进程而言的,表示在一个进程内部可以同时执行多个线程。

二、Java的Thread 线程类与 Runnable 接口 

Java语言中实现多线程的方法有两种:一种是继承 java.lang 包中的 Thread 类;另一种是用户在定义自己的类中实现 Runnable 接口。但不管采用哪种方法,都要用到Java语言类库中的Thread类以及相关的方法。

1. 利用Thread 类的子类来创建线程

Java语言的基本类库中已定义了 Thread 这个基本类,内置了一组方法,使程序利用该类提供的方法去产生一个新的线程、执行一个线程、终止一个线程的工作,或是查看线程的执行状态。
继承Thread类是实现线程的一种方法。Thread类的构造方法如下表:

构造方法功能说明
public Thread()创建一个线程对象,此线程对象的名称是“Thread-n”的形式,其中是一个整数。使用这个构造方法,必须创建Thread类的一个子类并覆盖其run()方法
public Thread(String name)创建一个线程对象,参数name指定了线程的名称
public Thread(String name)创建一个线程对象,此线程对象的名称是“Thread-n”的形式,其中n是一个整数。参数target 的run()方法将被线程对象调用,来作为其执行代码
public Thread ( Runnable target ,String name)功能同上,参数target的run()方法将被线程对象调用,来做为其执行代码。参数name指定了新创建线程的名称

要在一个 Thread 的子类里面激活线程,必粗准备好下列两件事。

(1)此类必须是继承自 Thread 类。

(2)线程所要执行的代码必须写在 run() 方法内。

语法如下:

calss 类名 extends Thread      //从 Thread 类派生子类
{类中的成员变量;类中的成员方法;修饰符 run()               //覆盖父类 Thread 里的 run() 方法{线程的代码}
}

run() 方法规定了线程要执行的任务,但一般不是直接调用 run() 方法,而是通过线程里面的 start() 方法来启动线程。

代码示例:利用 Thread 类的子类来创建线程

// 定义一个类,继承 Thread 类
class MyThread extends Thread {// 重写 run 方法,这个方法将在线程启动后执行@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - " + i);try {// 让线程睡眠一段时间,模拟实际工作Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class Main {public static void main(String[] args) {// 创建线程对象MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();// 启动线程thread1.start();thread2.start();// 主线程的工作for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - " + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

2. 用 Runnable 接口来创建线程

在Java中,除了通过继承 Thread 类来创建线程外,更常见的做法是使用 Runnable 接口来创建线程。这是因为Java不支持多重继承,而一个类可能已经继承了其他类,这时候就无法再继承Thread 类来创建线程。此外,使用 Runnable接口的方式更加符合面向对象编程的“单一职责原则”,因为线程的任务(即run()方法的内容)和线程的管理(如启动、等待、停止等)被分离到了不同的类中。

用 Runnable 接口来创建线程方法如下:

首先,你需要创建一个实现了Runnable接口的类。这个类必须实现run()方法,该方法包含了线程要执行的任务。

public class MyRunnable implements Runnable {  @Override  public void run() {  // 这里是线程要执行的任务  for (int i = 0; i < 5; i++) {  System.out.println("线程 " + Thread.currentThread().getId() + " 正在运行,计数 " + i);  try {  Thread.sleep(100);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  
}

然后,你需要创建一个Thread对象,并将实现了Runnable接口的类的实例作为参数传递给Thread类的构造函数。

public static void main(String[] args) {  MyRunnable myRunnable = new MyRunnable();  Thread thread = new Thread(myRunnable);  thread.start();  
}

通过上面的介绍,我们知道有两种创建线程对象的方式,这两种方式各有特点。

直接继承Thread类的特点是:编写简单,可以直接操纵线程;但缺点是若继承 Thread 类,就不能再继承其他类。
使用Runnable接口的特点是:可以将Thread类与所要处理的任务的类分开,形成清晰的模型;还可以从其他类继承,从而实现多重继承的功能。

另外,若直接使用Thread类,在类中 this 即指当前线程;若使用 Runnabe 接口,要在 * 类中获得当前线程,必须使用 Thread.currentThread() 方法。

三、线程之间的通信

多线程的执行往往需要相互之间的配合。为了更有效地协调不同线 程的工作,需要在线程间建立沟通渠道。
java.lang.Object 类的 wait()notify() 等方法为线程间的通信提供了有效手段。

如下表所示:

方法功能说明
public final void wait()如果一个正在执行同步代码(synchronized)的线程A执行了wait()调用(在对象x上),该线程暂停执行而进入对象x的等待队列,并释放已获得的对象x的互斥锁。线程A要一直等到其他线程在对象x上调用notify()或notifyA1l()方法,才能够在重新获得对象x的互斥锁后继续执行(从wait()语句后继续执行)
public void notify()唤醒正在等待该对象互斥锁的第一个线程
public void notifyAll()唤醒正在等待该对象互斥锁的所有线程,具有最高优先级的线程首先被唤醒并执行

注意:对于一个线程,若基于对象 x 调用 wait() 或 notify() 方法,该线程必须已经获得对象 x 的互斥锁。换句话说,wait() 和 notify() 只能在同步代码块里调用。

四、多线程的同步控制 

在 Java 多线程编程中,同步控制是非常重要的。它确保了多个线程在访问共享资源时不会发生冲突或数据不一致的问题。其主要通过 synchronized 关键字和 Lock 接口实现。

1. 使用synchronized 关键字

synchronized关键字可以修饰方法或者代码块,用于控制多线程对共享资源的访问。当一个线程获得锁时,其他线程需要等待该线程释放锁后才能继续执行。

示例:

public class SynchronizedDemo {private int count = 0;// 修饰方法public synchronized void add() {count++;}// 修饰代码块public void subtract() {synchronized (this) {count--;}}
}

2. 使用 Lock 接口

Lock接口提供了比synchronized更灵活的锁定机制,可以实现公平锁和非公平锁。公平锁表示锁的获取顺序是按照线程请求锁的顺序来分配的,而非公平锁则不保证这一点。

示例:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockDemo {private int count = 0;private Lock lock = new ReentrantLock();public void add() {lock.lock(); // 加锁try {count++;} finally {lock.unlock(); // 释放锁}}public void subtract() {lock.lock(); // 加锁try {count--;} finally {lock.unlock(); // 释放锁}}
}

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

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

相关文章

Java高手的30k之路|面试宝典|精通多线程(四)- 并发编程

并发编程中的问题 死锁 死锁是指在两个或多个线程的执行过程中&#xff0c;由于每个线程都在等待其他线程持有的资源&#xff0c;而无法继续执行&#xff0c;导致所有这些线程都处于阻塞状态&#xff0c;无法继续运行。 死锁的四个必要条件 根据Coffman的条件&#xff0c;死…

swagger下载文件名中文乱码、swagger导出文件名乱码、swagger文件导出名称乱码、解决swagger中文下载乱码bug

文章目录 一、场景描述&#xff1a;swagger导出文件名称乱码二、乱码原因三、解决方法3.1、方法一、在浏览器中输入地址下载3.2、方法二、swagger升级为2.10.0及以上 四、可能遇到的问题4.1、DocumentationPluginsManager.java:152 一、场景描述&#xff1a;swagger导出文件名称…

springboot与flowable(7):流程变量

一、启动时添加流程变量 拿第一个流程图举例&#xff0c;创建一个新的流程定义。 Testvoid contextLoads() {DeploymentBuilder deployment repositoryService.createDeployment();deployment.addClasspathResource("process01/FirstFlow.bpmn20.xml");deployment.…

android | MemoryLeakMonitor.jar is not exist! 目前还是存在这个问题,好像解决不到

2024了&#xff0c;用的华为的老机子 navo3 真机测试&#xff0c;目前还是这个渲染问题&#xff1a;滑动验证页面 MemoryLeakMonitor.jar is not exist! Software rendering doesnt support hardware bitmaps gpu的渲染问题&#xff1a; 这条信息“Software rendering doesnt…

动态规划-简单多状态dp问题 -- 删除并获得点数

动态规划-简单多状态dp问题 – 删除并获得点数 文章目录 动态规划-简单多状态dp问题 -- 删除并获得点数题目重现读懂题目算法流程示例代码 题目重现 题目链接&#xff1a;删除并获得点数 - 力扣 给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#…

用画图,将2张图片,合并成 一张图片 + 压缩体积

合并 第一步&#xff1a;选中要做比较的两张图片其中一张&#xff0c;单击鼠标右键&#xff0c;选择“打开方式--画图”。 第二步&#xff1a;如果图片过大&#xff0c;占据了整个屏幕不好观察&#xff0c;用右下角的标尺&#xff0c;缩小视图 第三步&#xff1a;鼠标左键按住…

HTTP学习记录(基于菜鸟教程)

文章目录 1.简介1.1常用的HTTP方法1.2Http版本1.3注意事项 2.Https3.Http消息结构3.1客户端请求消息3.2响应消息 4.常见的响应头5.HTTP状态码6.Http content-type在这里插入图片描述 7.MIME类型8.HTTP2 1.简介 Http&#xff0c;被称为超文本传输协议&#xff0c;HyperText Tran…

【Java核心技术10】Java数组详解

引言 在Java编程中&#xff0c;数组是一种非常重要的数据结构&#xff0c;它允许我们存储同一类型的多个元素&#xff0c;并通过索引快速访问这些元素。本文将详细讲解Java数组的基本概念、创建、初始化、访问和修改&#xff0c;以及多维数组和数组的常见操作。 无论你是Java的…

训练营第四十二天| 583. 两个字符串的删除操作72. 编辑距离647. 回文子串516.最长回文子序列

583. 两个字符串的删除操作 力扣题目链接(opens new window) 给定两个单词 word1 和 word2&#xff0c;找到使得 word1 和 word2 相同所需的最小步数&#xff0c;每步可以删除任意一个字符串中的一个字符。 示例&#xff1a; 输入: "sea", "eat"输出: …

如何优雅的一键同步OpenHarmony代码到gitlab?请关注【itopen:gitlab_sync】

itopen组织&#xff1a;1、提供OpenHarmony优雅实用的小工具2、手把手适配riscv qemu linux的三方库移植3、未来计划riscv qemu ohos的三方库移植 小程序开发4、一切拥抱开源&#xff0c;拥抱国产化 一、概述 项目中有需求要将 OpenHarmony 整套代码同步到项目的 gitlab…

nginx地址重写rewrite

nginx地址重写rewrite 1.nginx重写rewrite 1.rewrite相关语句 ifrewritesetreturn 2.if语句 应用环境 server,location语法 if () #可以支持&#xff1a; ~ #正则匹配(区分大小写) ~* #正则匹配(不区分大小写) !~ #正则不匹配(区分大小写) !~* #正则不匹配…

使用fetch加载阿里云的在线json 出现403错误

在做地图项目的时候&#xff0c;引用了阿里云的在线JSON地图数据。 问题描述&#xff1a; 但是本地开发使用fetch请求json地址的时候接口却出现了403错误&#xff0c;把地址直接复制到浏览器上却能正常打开。 https://geo.datav.aliyun.com/areas_v3/bound/330000_full.json …

06-操作元素

在前面的文章中重点介绍了一些元素的定位方法&#xff0c;定位到元素后&#xff0c;就需要操作元素了。本篇通过简单案例来介绍app应用中的一些常用操作。 一、案例介绍 下面列表中有四个字典&#xff0c;每个字典中的num1代表第一个操作数&#xff0c;num2代表第二个操作数&a…

力扣 面试题17.04.消失的数字

数组nums包含从0到n的所有整数&#xff0c;但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗&#xff1f; 示例 1&#xff1a; 输入&#xff1a;[3,0,1] 输出&#xff1a;2 示例 2&#xff1a; 输入&#xff1a;[9,6,4,2,3,5,7,0,1] 输出&#x…

GIT----使用技巧之保存现场回退新建分支继续开发

GIT----使用技巧之保存现场回退新建分支继续开发 前言&#xff1a; 故事是这样的&#xff0c;有一个比较复杂的项目使用的是STM32F103VCT6&#xff08;资源flash-256k,RAM-48k&#xff09;,开发到一半发现RAM不够用了&#xff0c;换容量更大的芯片STM32F103VGT6&#xff08;资源…

再谈量化策略失效的问题

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

【qt5生成软件-can卡-上位机-无法加载ControlCAN.dll错误代码(0xc0150002)等相关问题-WIN11系统-尝试解决】

【qt5生成软件-无法加载ControlCAN.dll&错误代码0xc0150002&#xff1a;-等相关问题-WIN11系统-尝试解决-总结整理】 1.前言2.环境说明3.问题说明4.尝试方法总结&#xff08;1&#xff09;更新支持包c库&#xff08;2&#xff09;更新USB相关驱动&#xff08;3&#xff09;…

Sqlite3数据库基本使用

一、基本概念 数据&#xff1a;能够输入计算机并能被计算机程序识别和处理的信息集合 数据库&#xff1a;长期存储在计算机内、有组织的、可共享的大量数据的集合 DBMS&#xff1a;位于用户与操作系统之间的一层数据管理软件&#xff0c;用于操纵和管理数据库 二、安装 在线…

一文读懂Java线程池之自定义线程池、设置合适的线程数量、线程池阻塞队列、线程拒绝策略

在上篇我们学习了线程池各个参数的含义,线程池任务处理流程,使用线程池的好处等内容,本篇我们学习如何创建一个适合我们业务的线程池。为此,我们有必要先学习一下如何大概确定我们线程池核心线程数、怎么设置阻塞队列的类型与大小、当线程池没有能力处理任务了该如何使用拒…

JS中操作符是什么

在JavaScript中&#xff0c;操作符是用于执行特定任务&#xff08;如加法、减法、比较等&#xff09;的特殊符号。这些操作符根据其功能可以分为几类&#xff1a; 算术操作符&#xff1a; &#xff1a;加法-&#xff1a;减法或取反*&#xff1a;乘法/&#xff1a;除法%&#xf…