Java线程基础知识汇总

进程与线程

什么是进程?

进程是指在计算机中运行的程序的实例。它是操作系统进行资源分配和调度的基本单位。一个进程可以包含多个线程,每个线程都共享该进程的资源,如内存、文件和打开的网络连接等。每个进程都有自己的地址空间,即独立的内存区域。

什么是线程?

线程是进程内的执行单元,也是CPU的最小执行单元。一个进程可以包含多个线程,每个线程执行不同的任务。线程共享进程的资源,包括内存、文件和打开的网络连接等。线程之间通过共享内存进行通信,因此比进程间通信更高效。由于线程共享同一进程的地址空间,所以多线程之间的切换更快。

例如:我们启动JVM运行一个Java程序,其实就是启动了一个 JVM 的进程。在JVM的进程中,又包含了main :主线程Reference Handler:清理线程Finalizer:线程(用于调用对象 的finalize()方法)等线程。

线程与进程的区别?

  1. 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位;
  2. 资源开销:每个进程都有独立的代码副本和数据空间,进程之间的切换,资源开销较大;线程可以看做轻量级的进程,每个线程都有自己独立的运行栈和程序计数器,线程之间切换,资源开销小;
  3. 内存分配:同一进程内的所有线程共享本进程的内存空间和资源;进程之间的内存空间和资源相互独立;
  4. 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响;一个线程崩溃,会导致整个进程退出。所以多进程要比多线程健壮;
  5. 执行过程:每个独立的进程有程序运行的入口和程序出口。但是线程不能独立执行,必须依存在应用程序(进程)中,由应用程序提供多个线程执行控制;
  6. 包含关系:一个进程内包含有多个线程,在执行过程,线程的执行不是线性串行的,而是多条线程并行共同完成;

单线程与多线程

单线程:单线程指的是程序中只有一个主线程在执行任务。在单线程模型中,任务按顺序依次执行,每个任务必须等待前一个任务完成后才能执行。当一个任务发生阻塞(如等待I/O操作或进行复杂计算)时,整个程序都会被阻塞,无法执行其他任务。单线程模型简单,易于编写和调试,但其执行效率受限于单个线程的处理能力。

多线程:多线程指的是程序中同时存在多个线程,并发地执行不同的任务。每个线程独立执行,可以同时进行多个任务。多线程模型可以充分利用多核处理器和资源,提高程序的性能和响应速度。不同的线程可以并行执行独立的任务,当某个线程发生阻塞时,其他线程仍然可以继续执行,不会阻塞整个程序。多线程编程需要注意线程安全、同步和数据共享等问题,避免出现竞态条件和死锁等并发问题。


线程的四种创建方式

为放方便整理,以下方法均采用匿名继承、匿名实现来完成。

1.方式一:继承 java.lang.Thread 类(线程子类)

//匿名子类
StringBuffer sb = new StringBuffer();
Thread t1 = new Thread() {@Overridepublic void run() {for(char i ='A';i<='E';i++) {sb.append(i);}}
};

2.方式二:实现 java.lang.Runnable 接口(线程执行类)

Thread t2 = new Thread( new Runnable() {		@Overridepublic void run() {for(char i ='a';i<='e';i++) {sb.append(i);}}
});

3.实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常

Thread t3 = new Thread(new FutureTask<String>(new Callable<String>() {@Overridepublic String call() throws Exception {for(int i = 1;i<=5;i++) {sb.append(i);}return sb.toString();}	
}));

4.线程池

线程池是线程中非常重要的一个知识点,此处不做详细介绍,只通过线程池创建一个线程对象。

ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(new Runnable() {	@Overridepublic void run() {String[] s= {"@","#","$","%","!"};for(int i =0;i<s.length;i++) {sb.append(s[i]);}}
});

5.线程的启动

通过调用Thread实例的start()方法启动新线程。
start()方法内部调用了一个private native void start0()方法,native修饰符表示这个方法是由JVM虚拟机内部的C代码实现的本地方法,由JVM根据当前操作系统进行本地实现。

Thread t1 = new Thread();
t1.start();

当线程启动后,它将在自己的执行路径上执行run()方法中的代码。


线程的休眠与优先级

线程的休眠

要使线程进入休眠状态,你可以使用Thread.sleep()方法。它使当前线程暂停执行一段指定的时间,然后再继续执行。
当线程调用 Sleep 后,它会进入阻塞状态,不会占用 CPU 资源。在指定的时间到达之前,线程不会被唤醒。Sleep 不会释放锁,因此其他线程无法访问被当前线程锁住的资源。

Thread.sleep()方法有两种重载形式:

sleep(long millis):接受一个以毫秒为单位的时间参数,表示线程要休眠的时间长度。
sleep(long millis, int nanos):接受一个以毫秒为单位和一个以纳秒为单位的时间参数,表示线程要休眠的时间长度。(不常使用)

 public static void main(String[] args) {System.out.println("主线程开始执行。。。");try {// 主线程休眠5秒Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程执行结束。。。");}

线程的优先级

在线程中,通过setPriority(int n)设置线程优先级,范围是1-10,默认为 5。
优先级高的线程被操作系统调度的优先级较高(操作系统对高优先级线程,调度更频繁)

Thread t1 = new MyThread() {@Overridepublic void run() {for(char i ='A';i<='E';i++) {sb.append(i);}}
};
Thread t2 = new Thread( new Runnable() {		@Overridepublic void run() {for(char i ='a';i<='e';i++) {sb.append(i);}}
});t1.setPriority(8); // 设置数字线程优先级=8t2.setPriority(1); // 设置字母线程优先级=1
// 启动子线程
t1.start();
t2.start();

注意:并不能代表,通过设置优先级来确保高优先级的线程一定会先执行,只是会提高一定的优先级。


线程的状态

在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自
动执行run()方法。run()方法执行完毕,代表线程的生命周期结束
在整个线程的生命周期中,线程的状态有以下6种:

  • new:新建状态,新创建的线程,此时尚未调用start()方法;
  • Runnable:运行状态,运行中的线程,已经调用了start()方法,线程正在或即将被执行;
  • Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
  • Waiting:等待状态,运行中的线程,因为sleep()方法、join()方法等方法的调用,进入等待;
  • Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
  • Terminated:终止状态,线程已经终止,因为run()方法已经执行完毕; 当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止。

线程的常见方法

1.线程的插队:join()方法

join() 方法是 Thread 类的实例方法,可以在一个线程中调用另一个线程的 join() 方法等待其执行完成,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。
join() 方法是基于协作的线程间等待,调用线程会等待被等待线程执行完毕,期间不会释放锁资源

		//创建并执行子线程MyThread myThread = new MyThread();myThread.start();//主线程调用myThread子线程的join方法//子线程插队,插入到	当前线程main的执行序列前//myThread.join();myThread.join(500);	//join()方法调用后,当子线程执行完毕后,主线程才会再执行System.out.println("主线程Main:执行完毕。。。");

join()方法的底层是利用wait()方法实现;

1.2.join()方法和sleep()方法的区别

  1. 两个方法都可以实现类似"线程等待"的效果,但是仍然有区别;
  2. join()是通过在内部使用synchronized + wait()方法来实现的,所以join()方法调用结束后,会释放锁;
  3. sleep()休眠没有结束前,不会释放锁;

2.线程的中断:interrupt()方法

interrupt() 方法用于中断正在运行的线程。当调用 interrupt() 方法时,它会将线程的中断状态设置为 “中断”,但并不会直接停止线程的执行。当线程被中断时,它可以选择如何响应中断。线程可以通过检查自己的中断状态并采取适当的行动来响应中断。例如,线程可以选择继续执行,或者可以选择终止自己的执行。
支持线程中断的方法(Thread.sleep() 、join()、wait()等方法)就是在监视线程的中断状态,一旦发现线程的中断状态值被置为“true”,就会抛出线程中断的异常InterruptedException,给WAITING或者TIMED_WAITING等待状态的线程发出一个中断信号,线程检查中断标识,就会以退出WAITING或者TIMED_WAITING等待状态;

public class Test5 {public static void main(String[] args) throws InterruptedException {System.out.println("main主线程:开始执行");        // 创建2个子线程Thread t1 = new Thread("线程1") {@Overridepublic void run() {System.out.println(getName() + ":开始执行");while(!isInterrupted()) {System.out.println(UUID.randomUUID());}System.out.println(getName() + ":结束执行");}};Thread t2 = new Thread("线程2") {@Overridepublic void run() {System.out.println(getName() + ":开始执行");while(!isInterrupted()) {System.out.println((int)(Math.random()*10000));}System.out.println(getName() + ":结束执行");}};// 启动子线程t1.start();t2.start();// 主线程休眠10毫秒Thread.sleep(10);// 10毫秒后,中断子线程1t1.interrupt();// 子线程1执行结束后,继续执行主线程t1.join();System.out.println("main主线程:结束执行");// 主线程执行结束后,中断子线程2// 子线程1的中断,不会影响子线程2t2.interrupt();}

3.线程的让出:yield()方法

yield() 方法用于提示线程调度器当前线程愿意放弃对 CPU 的使用,以便其他具有相同优先级的线程有机会执行。调用 yield() 方法会暂停当前正在执行的线程,并将执行机会交给其他具有相同优先级的线程。

	public static void main(String[] args) {Thread t1 = new Thread("线程1") {@Overridepublic void run() {for(char c = 'A';c<='Z';c++) {System.out.println(c);}}};Thread t2 = new Thread("线程2") {@Overridepublic void run() {//Thread.yield();for(int i = 1;i<=25;i++) {				System.out.println(i);Thread.yield();//让当前线程让出cpu}}};t1.start();t2.start();		}

需要注意的是,yield() 方法仅仅是提供了一种提示,实际的线程调度行为由 JVM 的实现和操作系统的调度器来决定。因此,对于不同的 JVM 和操作系统,yield() 方法的行为可能会有所不同。另外,yield() 方法不会释放线程持有的锁

4.线程的等待:wait()方法

wait() 方法是 Object 类的实例方法,需要在同步代码块或同步方法中调用,即在持有锁的情况下才能调用。该方法会使当前线程进入等待状态,释放对象锁资源,(会使当前线程让出持有的"this锁",允许其它线程参与竞争CPU执行权(this锁),而sleep的休眠过程中,当前线程不会让出持有锁!!!)等待其他线程调用相同对象的 notify() 或 notifyAll() 方法来唤醒。

//当前对象.wait(),配合synchronize锁使用
this.wait(1000);

5.线程的唤醒:notify()方法和notifyAll()方法

线程的唤醒指的是从等待状态(如调用了wait()方法)中唤醒线程,使其继续执行;
在Java中,线程的唤醒方法有:
notify()方法:随机唤醒等待的某个线程;
notifyAll()方法:唤醒全部等待线程;

synchronized (this) {while(true) {if(ticketNum<=0) {//System.out.println(Thread.currentThread().getName()+":没有票了");return;}else {//System.out.println(Thread.currentThread().getName()+"买了一张票,剩余:"+ --ticketNum);try {//让当前线程休息1000秒,模拟延迟//休眠过程中,当前线程不会让出“锁”,而是带着锁一起休眠					//Thread.sleep(1000);			//让当前持有this锁的对象等待,让出锁并等待this.wait(1000);} catch (InterruptedException e) {e.printStackTrace();}}	}
}

等待线程通过调用this.wait()方法进入等待状态,释放锁,并等待唤醒信号,等待线程将继续执行后续的代码。


守护线程

守护线程(Daemon Thread)是一种特殊类型的线程,它的存在并不会阻止 JVM 的退出。当所有的非守护线程都结束时,守护线程会自动终止。
在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程。

Long starTime = System.currentTimeMillis();
Thread t2= new Thread("线程2") {@Overridepublic void run() {try {Thread.sleep(5000);		} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("守护线程运行时间:"+(System.currentTimeMillis()-starTime));}};t2.setDaemon(true);//设置为守护线程t2.start();	try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程运行时间:"+(System.currentTimeMillis()-starTime));}
}

当主线程结束时,守护线程也会自动终止,即使它还在执行。
需要注意的是,守护线程不能用于执行一些需要完整性保证的任务,比如文件写入操作或数据库事务。因为守护线程在 JVM 退出时会被强制终止,可能导致任务不完整或数据丢失。守护线程通常用于执行一些后台任务或支持性工作,比如垃圾回收器线程。


总结

  • Java用Thread对象表示一个线程,通过调用start()启动一个新线程;
  • 线程调度由操作系统决定,程序本身无法决定调度顺序
  • Thread.sleep()可以把当前线程暂停一段时间。
  • 线程的状态有以下6种:New、Runnable、Blocked、Waiting、Timed Waiting、Terminated;
  • join()方法用于实现线程插队,调用完毕后会释放锁;sleep()方法用于实现线程休眠,调用完毕后不会释放锁。


---------------------
作者:杨树林_spring
来源:CSDN
原文:https://blog.csdn.net/HSQdePYZ/article/details/131721454
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

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

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

相关文章

FlowUs2024重磅革新预告:RAG技术赋能『问问AI』,笔记变现新纪元等你开启!

&#x1f389; 在FlowUs的广阔天地间&#xff0c;知识的边界被无限拓展&#xff0c;引领着一场场创新与收获的庆典&#xff01;&#x1f680; 随着一年间不断的精进与革新&#xff0c;FlowUs与众多用户并肩前行&#xff0c;在追求极致体验的道路上迈出坚实步伐。步入2024年&am…

WordPress项目教程:自动采集并发布,让你轻松实现网站内容更新

随着互联网的发展&#xff0c;越来越多的人开始关注自己的个人网站&#xff0c;通过网站展示自己的才华、分享知识、推广产品等。然而&#xff0c;个人网站的运营并非易事&#xff0c;尤其是内容更新方面。为了解决这个问题&#xff0c;今天我们将为大家推荐一款WordPress插件主…

minio直接通过地址访问无需服务器转发

背景 做网站有些图片、视频、js等资源&#xff0c;没有什么变化&#xff0c;想在网站上直接使用&#xff0c;前端拿到地址可直接访问获得&#xff0c;而不是通过后台转一道再获得&#xff0c;折腾了半天访问不到&#xff0c;从网上找资料挨个试&#xff0c;也没完全解决&#…

云计算期末综合测试题

云计算综合测试题 单选题填空题判断题简答题 单选题 这里选择题&#xff0c;直接以填空题展示&#xff0c;并给出解析 Bigtable是&#xff08;Google&#xff09;开发的分布式存储系统 解析&#xff1a;分布式结构化数据表Bigtable是Google基于GFS和Chubby开发的分布式存储系统…

君子小人的格局、境界

子曰&#xff1a;君子怀德&#xff0c;小人怀土&#xff1b;君子怀刑&#xff0c;小人怀惠。 直译&#xff1a;君子怀念道德&#xff0c;小人怀念乡土&#xff1b;君子关心法度&#xff0c;小人关心恩惠。 这里的君子与小人只是体现格局、境界的不同&#xff1b; 君子怀的是德…

DVWA 靶场 SQL Injection (Blind) 通关解析

前言 DVWA代表Damn Vulnerable Web Application&#xff0c;是一个用于学习和练习Web应用程序漏洞的开源漏洞应用程序。它被设计成一个易于安装和配置的漏洞应用程序&#xff0c;旨在帮助安全专业人员和爱好者了解和熟悉不同类型的Web应用程序漏洞。 DVWA提供了一系列的漏洞场…

sklearn之各类朴素贝叶斯原理

sklearn之贝叶斯原理 前言1 高斯朴素贝叶斯1.1 对连续变量的处理1.2 高斯朴素贝叶斯算法原理 2 多项式朴素贝叶斯2.1 二项分布和多项分布2.2 详细原理2.3 如何判断是否符合多项式贝叶斯 3 伯努利朴素贝叶斯4 类别贝叶斯4 补充朴素贝叶斯4.1 核心原理4.2 算法流程 前言 如果想看…

docker nacos2.3.2安装填坑

#nacos2.3.2安装# git clone https://github.com/nacos-group/nacos-docker.git cd nacos-docker #安装mysql8单机版 docker-compose -f example/standalone-mysql-8.yaml up #https://hub.docker.com 抽风# 因为网络原因&#xff0c;nacos/nacos-server 默认安装的最后版本…

Python封装cryptography对称加密方法

安装依赖 pip install cryptography实现方法 from cryptography.fernet import Fernetclass SymmetricEncryption(object):对称加密算法def __init__(self, secret_key=None

学习笔记——交通安全分析08

目录 前言 当天学习笔记整理 4信控交叉口交通安全分析 结束语 前言 #随着上一轮SPSS学习完成之后&#xff0c;本人又开始了新教材《交通安全分析》的学习 #整理过程不易&#xff0c;喜欢UP就点个免费的关注趴 #本期内容接上一期07笔记 当天学习笔记整理 4信控交叉口交…

天马学航——智慧教务系统(移动端)开发日志四

天马学航——智慧教务系统(移动端)开发日志四 日志摘要&#xff1a;优化了教师端界面的UI&#xff0c;更新了教师端添加课程&#xff0c;提交成绩等功能&#xff0c;修复了一些已知的BUG 1、教师添加课程设计 教师在此界面添加课程&#xff0c;并将数据提交后端进行审核 界…

代码随想录算法训练营第四十七天|LeetCode123 买卖股票的最佳时机Ⅲ

题1&#xff1a; 指路&#xff1a;123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 思路与代码&#xff1a; 买卖股票专题中三者不同的是Ⅰ为只买卖一次&#xff0c;Ⅱ可多次买卖&#xff0c;Ⅲ最多可买卖两次。那么我们将买买卖行为分为五个状态部分(…

LeetCode 每日一题 2024/6/17-2024/6/23

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 6/17 522. 最长特殊序列 II6/18 2288. 价格减免6/19 2713. 矩阵中严格递增的单元格数6/20 2748. 美丽下标对的数目6/21 LCP 61. 气温变化趋势6/22 2663. 字典序最小的美丽字…

WAAP的特性、功能以及优势

随着互联网技术的快速发展&#xff0c;Web应用程序和API已经成为企业日常运营中不可或缺的部分。然而&#xff0c;与此同时&#xff0c;网络攻击手段也愈发复杂和隐蔽&#xff0c;给企业的数据安全带来了严峻的挑战。为了应对这一挑战&#xff0c;WAAP&#xff08;WebApplicati…

fastapi+vue3+primeflex前后端分离开发项目第一个程序

安装axios axios是用来请求后端接口的。 https://www.axios-http.cn/docs/intro pnpm 是一个前端的包管理工具&#xff0c;当我们需要给前端项目添加新的依赖的时候&#xff0c;就可以使用pnpm install 命令进行安装。 pnpm install axios安装 primeflex primeflex是一个cs…

知乎客户端跨平台-Hybrid-调试实战

在开发上述功能的过程中&#xff0c;发现了 flipper 这个工具 flipper 提供了一个桌面客户端&#xff0c;然后这个桌面客户端提供了一个和手机客户端通信的机制&#xff0c;免去了 socket 服务的开销&#xff0c;依靠这个通信机制&#xff0c;我们可以把上述的功能复制过来 基…

Python数列求和

1 问题 如何用python解决数学问题&#xff1f;如何用python数列求和&#xff1f; 2 方法 代码清单 1 Courier New字体&#xff0c;23磅行间距>>> def sum_num(): input_num input("输入一个0-9的整数:") try: input_num int(input_num) …

AI时代的音乐革命:创作更简单,灵魂在哪里?

#AI在创造还是毁掉音乐# 我是李涛&#xff0c;一名音乐创作者&#xff0c;最近一直在思考一个问题&#xff1a;AI到底是在创造音乐&#xff0c;还是在毁掉音乐&#xff1f; 几个月前&#xff0c;我第一次接触到AI音乐创作工具。它让我震惊&#xff0c;只需要输入几个关键词&a…

Apollo9.0 PNC源码学习之Planning模块(一)—— 规划概览

0 前言 规划本质就是搜索问题,数学角度来看就是寻找函数最优解 规划模块复杂的就是相关的逻辑处理和过程计算 对于规划的三维问题,目前解决方案:降维+迭代 将SLT问题分解为ST和SL二维优化问题:在一个维度优化之后,再另一个维度再进行优化,最后整合成三维的轨迹。 虽然降…

人机交互中的真问题与假问题

在人机交互领域&#xff0c;存在一些真正的挑战和问题&#xff0c;这些问题影响着如何有效地设计、开发和使用人机界面和交互系统。具体涉及&#xff1a; 人机交互系统需要尽可能自然和普适&#xff0c;以便用户可以轻松理解和使用。例如&#xff0c;语音识别系统需要能够准确地…