java中的线程&创建线程的三种方式

创建线程的8种方法,90%的人不知道 n\n示例代码\nclass MyRunnable implements Runnable {\n @Override\n public void run() {\n System.out.println(\"线程名称:\" + Thread.currentThread().getName() + \" 正在执行任务\");\n\n缺点: 不适合复杂调度场景。\n }\n}\n \n总结\n以上就是 Java 中创建线程的 8 种方法,每一种方法都有其适用场景和优缺点。 阅读详情

java语言里的线程本质上就是操作系统的线程,他们是一 一对应的

线程生命周期

线程状态转换图—— 五态模型
  1. 初始状态: 线程已经被创建,但是还没有分配CPU执行。 这个状态属于编程语言特有的,不过这里所谓的被创建,仅仅时在编程语言层面被创建,而在操作系统层面,真正的线程还没创建。
  2. 可运行状态:初始状态线程执行start()方法,线程具备CPU的执行资格,没有CPU的执行权。 这种状态下,真正的操作系统线程已经被成功创建了,所以可以分配CPU执行了。
  3. 运行状态: 处于可运行状态的线程得到CUP执行权。 当有空闲的CPU时,操作系统会将其分配给一个处于可运行状态的线程,被分配到CPU的线程状态就转换成了运行状态。
  4. 休眠状态:运行状态的线程释放CPU的执行权。 运行状态的线程如果调用一个阻塞的API(sleep,wait)或者等待某个事件(例如条件变量),那么线程的状态就会转换到休眠状态,同时释放CPU使用权,休眠状态的线程永远没有机会获得 CPU使用权。当等待的事件出现了(sleep到期,wait执行notif),线程就会从休眠状态转换到可运行状态。
  5. 终止状态: 线程执行完或者出现异常或者调用API强制结束。
java中的线程周期

Java语言中线程共有六种状态,分别是:

  1. NEW(初始化状态) : Java刚创建出来的Thread对象。
  2. RUNNABLE(可运行/运行状态) : Thread执行start()方法。
  3. BLOCKED(阻塞状态),WAITING(无时限等待),TIMED_WAITING(有时限等待) : RUNNABLE状态的线程调用wait()、join()、sleep()方法。
  4. TERMINATED(终止状态): run()方法执行完,或者意外中断。

BLOCKED,WAITING,TIMED_WAITING 是上面提到的休眠状态。Java线程处于这些状态那么这个线程就永远没有CPU的使用权。

线程可以通过isInterrupted()方法,检 测是不是自己被中断了。

创建一个线程

Java刚创建出来的Thread对象就是NEW状态,而创建Thread对象主要有两种方法。一种是继承Thread对 象,重写run()方法。示例代码如下:

方式一: 继承Thread

//	⾃定义线程对象 
class MyThread extends Thread{
    public void run() {				
        //	线程需要执⾏的代码	
    } 
} 
//	创建线程对象 
MyThread myThread =	new	MyThread();

方式二: 实现Runnable接口

//实现Runnable接⼝ 
class Runner implements Runnable {		
    @Override		
    public void run(){				
        //线程需要执⾏的代码		
    } 
} 
//创建线程对象 
Thread thread = new Thread(new Runner());

方式三:实现Callable接口

//实现Runnable接⼝ 
class Runner implements Callable<String> {		
    @Override
     public String call() throws Exception {
          //线程需要执⾏的代码		
         return null;
     }
}

//创建 FutureTask
 FutureTask<String> ft1 = new FutureTask(new Runner());
//执行这个任务
Thread t1 = new Thread(ft1);
t1.start();
//获取返回值
t1.get();

方法三实质上也是实现了Runnable接口,因为FutureTask实现了Runnable接口

Future接口提供的方法:

//	取消任务 
boolean	cancel(boolean	mayInterruptIfRunning); 
//	判断任务是否已取消		
boolean	isCancelled(); 
//	判断任务是否已结束 
boolean	isDone(); 
//	获得任务执⾏结果 
get(); 
//	获得任务执⾏结果,⽀持超时 
get(long timeout, TimeUnit unit);

这两个get()方法都是阻塞式的,如果被调用的时候,任务还没有执行完,那么调用get()方法的线程会阻塞,直到任务执行完才会被唤醒。

  • Java刚创建出来的Thread对象就是NEW状态
  • NEW状态的线程不会被操作系统调度,因此不会执行
  • NEW状态的线程调用start()会进入RUNNABLE状态
stop()与interrupt()的区别

stop()会杀死线程,如果线程持有ReentrantLock锁,被stop()的线程并不会自动调用ReentrantLock的unlock()去释放锁,那其他线程就再也没机会获得ReentrantLock锁。所以该方法就不建议使用了,类似的方法还有suspend()和resume()方法,这两个方法同样也都不建议使用。

interrupt()仅仅是通知线程,线程有机会执行一些后续操作,同时也可以无视这个通知。

为什么要使用多线程

提高程序的性能: 降低延迟,提高吞吐量。

提高性能的方式:1优化算法;2将硬件的性能发挥到极致

在并发编程领域,提升性能本质上就是提升硬件的利用率,具体来说就是提升I/O的利用率和CPU的利用率。

如果CPU和I/O设备的利用率都很低,那么可以尝试通过增加线程提高吞吐量。

创建多少线程合适?

我们的程序一般都是CPU计算和I/O操作交叉执行的,由于I/O设备的速度相对于CPU来说都是很慢的,所以大部分情况下,I/O操作的执行时间相对于CPU计算来说都非常长,这种场景我们一般都成为I/O密集型程序和CPU密集型程序,计算最近线程数的方法是不同的。

对于CPU密集型的计算场景,理论上“线程的数量=CPU核数”就是最合适的。不过在工程上,线程的数量一般会设置为"CPU核数+1" ,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证CPU的利用率。

最佳线程数 =CPU 核数 +1

对于I/O密集型的计算场景,比如前面我们的例子中,如果CPU计算和I/O操作的耗时是1:1,那么2个线程是 最合适的。如果CPU计算和I/O操作的耗时是1:2,那多少个线程合适呢?是3个线程,如下图所示:CPU在 A、B、C三个线程之间切换,对于线程A,当CPU从B、C切换回来时,线程A正好执行完I/O操作。这样CPU 和I/O设备的利用率都达到了100%。

最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

设置线程数的原则: 将硬件的性能发挥到极致。

为什么局部变量是线程安全的?

调用栈结构

线程与调用栈的关系图

每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里面,不会共享,所以自然也就没有并发问题。

局部变量的作用域是方法内部,也就是说当方法执行完,局部变量就没用了,局部变量和方法同生共死。

局部变量是和方法同生共死的,一个变量如果想跨越方法的边界,就必须创建在堆里。

调用栈与线程

两个线程可以同时用不同的参数调用相同的方法。

每个线程都有自己独立的调用栈。

线程封闭 : 仅在单线程内访问数据。不存在多线程的数据共享。

栈溢出的原因:

因为每调用一个方法就会在栈上创建一个栈帧,方法调用结束后就会弹出该栈帧,而栈的大小不是无限的 ,所以递归调用次数过多的话就会导致栈溢出。而递归调用的特点是每递归一次,就要创建一个新的栈帧 ,而且还要保留之前的环境(栈帧),直到遇到结束条件。所以递归调用一定要明确好结束条件,不要出现死循环,而且要避免栈太深。

解决方法:

  1. 简单粗暴,不要使用递归,使用循环替代。缺点:代码逻辑不够清晰;
  2. 限制递归次数;
  3. 使用尾递归,尾递归是指在方法返回时只调用自己本身,且不能包含表达式。编译器或解释器会把尾递归做优化,使递归方法不论调用多少次,都只占用一个栈帧,所以不会出现栈溢出。然而,Java没有尾递归优化。
Java创建线程的七种方法,全网最全面总结~ 属于基础的面试问题,一定要能够回答全哦~ 阅读详情

相关推荐

Java高并发编程实战14,强制禁止,线程池不允许使用Executors创建

大家好,我是哪吒。在程序开发中,高并发场景越来越多,线程池首当其冲。简单回顾一下:《阿里巴巴 Java 开发手册》强制禁止,线程池不允许使用Executors创建,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能让编写代码的工程师更加明确线程池的运行规则,避免资源耗尽的风险。Executors返回的线程池对象的弊端如下:好吧,昨天,我还通过创建了一个线程池,看来以后要注意了。具体分析以下【说明】中的内容,以FixedThreadPool为例,允许的请求队列长度为Integer.MAX

学Java,找哪吒 1760

JAVA线程

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。

互联网底层民工的博客 1万+

自由曲面与衍射光学元件DOE:从基础理论到工业应用的全景解析

本文全景解析了自由曲面与衍射光学元件的核心理论与工业应用。文章深入探讨了自由曲面在非对称光学设计中的灵活性及其数学描述与制造挑战,并阐述了衍射光学元件通过微纳结构进行波前相位调制的原理与多样化功能。两者与经典光学互补融合,共同构成了高端成像、智能照明、激光加工及AR/VR等前沿领域的底层技术支柱。

devops8pract的博客 390

java 线程详解

一、线程的基本概念 一个线程就是在进程中的一个单一的顺序控制流. 而单个进程可以拥有多个并发执行的任务,每个任务都好像有自己的CPU一样,而其底层的机制就是切分CPU的时间,也就是CPU将轮流给每个任务分配其占用时间。 每个任务都觉得自己在一直占用CPU,而事实上是将CPU时间划分成片段分配给所有的任务。 在多个CPU的环境下,多线程的运作,可以极大的提供程序的运行速度,这就是线程存在的意...

muranfei的博客 1万+

Android——Handler详解

1. 简介 Handler是一套 Android 消息传递机制 可以说只要有异步线程与主线程通信的地方就一定会有 Handler。 在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理 使用Handler消息传递机制主要是为了多个线程并发更新UI的同时,保证线程安全 2. 相关概念解释 Handler、Message、Message Queue、Looper Android消息机制: 以Handler的sendMes

Yawn 7万+

基于Simulink的自适应反步法(Adaptive Backstepping)控制​

本文针对非线性系统控制中未知参数和强非线性耦合的挑战,提出基于Simulink的自适应反步法解决方案。通过融合参数在线估计与反步递归设计,该方法显著提升了永磁同步电机(PMSM)速度控制的鲁棒性,使跟踪误差从20%降至3%,参数估计收敛时间小于0.5秒。文章详细阐述了从算法推导到Simulink实现的完整流程,包括系统架构设计、核心算法推导(以PMSM为例)、模块化建模方法、参数优化策略以及工程部署建议。提供的自适应控制器MATLAB代码和关键参数表可直接应用于实际工程,为电机驱动、机械臂等非线性系统控制提

xiaoheshang_123的博客 101

Java线程系列详解

1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法。3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性。4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。

wang_yong_hui_1234的博客 5787

python全栈入门到实战【基础篇 02】环境搭建:Python解释器与PyCharm、VSCode编辑器安装配置详解

本文详解Python环境搭建步骤。首先安装解释器(运行代码核心),需从官网下载,选3.8及以上版本(勿选2.x)。Windows安装必勾“Add Python.exe to PATH”,验证用`python --version`;Mac选universal2安装包,验证用`python3 --version`。 编辑器二选一:PyCharm社区版(免费,新手推荐),安装后需配置解释器路径;VSCode轻量,需装Python插件并选择解释器。

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 5万+

Java 线程 基础知识总结

文章目录线程基础生产消费者模型,synchronizedjoin方法+死锁(哲学家进餐问题)join和锁死锁有趣的Timer类Timer多线程的一个运行实例 初步认识下线程,线程怎么去实现(通过继承Thread或者实现Runnable接口) 线程基础 很不严谨的说,线程是什么?线程就是为了让很多个东东并发执行,大大的提高程序执行的效率啊 三个非常重要的概念: 程序:一组写好了的静态代码块(就我...

Zhangguohao666的博客 6812

第03章 Hermes Agent 核心原理

环境跑通之后,读者会很自然地想问:Hermes 的内部到底是怎么转起来的?为什么它能做到"越用越聪明"?长对话不会忘记上下文吗?这一章用尽量短的篇幅把核心原理讲清楚。理解原理不是为了纸上谈兵,而是为了在第7章实战时能判断"为什么 Hermes 这么响应"以及"我应该把哪类任务交给它"。

亢少军的博客 141

OpenCV 实战篇——如何测算出任一副图片中的物体的实际尺寸?传感器尺寸与像元尺寸的关系?

本文介绍了通过相机图像测算物体实际尺寸的方法,重点分析了传感器参数与成像质量的关系。文章首先给出了物体实际尺寸的计算公式,涉及像素尺寸、距离、分辨率等参数。然后详细解析了传感器尺寸与像元尺寸的关系,指出相同分辨率下更大的传感器能获得更好的成像质量。此外还介绍了光学尺寸、像素尺寸对成像的影响,以及帧率的概念。最后提及了相机标定的重要性,并概述了从像素坐标系转换到世界坐标系的基本思路。这些知识为通过相机图像进行精确测量提供了理论基础。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 1336

Java创建线程的三种方式,以前只知道两种,现在添加一种Callable与FutureTask创建的方式

一共有以下三种方式: 1.继承Thread 2.实现Runnable 3.实现Callable,并FutureTask包装 线程启动方式均是使用start()方法 先阐述优缺点: 1和2,3: 2,3优于1是使用了借口,留出来了继承的位置 1优于2,3是使用简单,实现简单 2和3: 我认为3的实现比2更复杂一些,因为2的方法效果单一 但正所谓一分耕耘一份收获,程序也是如此 3的call()方法比2...

博客 260

基于Simulink的加速度计振动分析:单自由度质量-弹簧-阻尼系统

摘要:基于Simulink的加速度计单自由度振动分析 本文研究了加速度计的单自由度(SDOF)质量-弹簧-阻尼系统模型,通过MATLAB/Simulink平台建立了完整的振动分析仿真系统。文章详细阐述了理论建模过程,推导了系统运动微分方程和传递函数,并提供了Simulink模型搭建的具体步骤和MATLAB实现代码。研究通过正弦扫频、随机振动和冲击响应三种典型输入信号,分析了系统的幅频特性、相频特性及时域响应,验证了加速度计的工作机理。仿真结果表明,该方法能有效模拟加速度计在不同激励条件下的动态特性,为传感器

AI的内部世界的博客 163

每日分享之-配置文件&日志&多线程

在企业开发过程中,我们习惯把一些需要灵活配置的数据放在一些文本文件中,而不是在Java代码写死 我们把这种存放程序配置信息的文件,统称为配置文件配置文件一般要求有明确的格式,以方便读写操作。

jayzhang_的博客 2523

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

java线程初始

目标 线程的概念 线程和进程的区别 串行、并行和并发的概念 线程的实现 线程的生命周期 线程的常用方法 线程的概念 程序和进程的区别 程序是静态的,进程是动态的,程序是存储在某种介质上的二进制代码,进程对应了程序的执行过程,系统不需要为一个不执行的程序创建进程,一旦进程被创建,就处于不断变化的动态过程中,对应了一个不断变化的上下文环境。 程序是永久的,进程是暂时存在的。程序的永久性是相对于进程而言的,只要不去删除它,它可以永久的存

weixin_46701812的博客 1614

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

每日筑基之配置文件&日志&多线程

简单来说就是电脑在做一件事,单线程,在做一件事;多线程,在做多件事。

abcdewn的博客 2272

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

知识梳理:字节流(InputStream&OutputStream) & 字符类 & RandomAccessFile & 对象序列化 & 线程创建与启动 & 守护线程 线程中断

字节流(InputStream&amp;OutputStream) &amp; 字符类 &amp; RandomAccessFile &amp; 对象序列化 &amp; 线程创建与启动 &amp; 守护线程 线程中断 目录:字节流(InputStream&amp;OutputStream)&amp;字符类&amp;RandomAccessFile&amp;对象序列化&amp; 线程创建与启动&amp;守护线程 线程中断 字节流(InputStream&amp;OutputStream) InputStrea

weixin_47255857的博客 1143

java文件操作,线程创建,方法传递

1. 文件 1.File类: 既可以表示文件 还可以表示目录。 2.构造方法: File(pathname) 3.路径问题: 如果使用的是\ 需要两个表示路径。 ① 绝对路径: 从盘符开始的路径。d:\1.txt. ② 相对路径: 从某个路径开始的路径 1.txt 当没有任何修饰时,相对于项目的路径。 4.常用方法: getAbsolutePath() 获取文件的绝对路径 代码: public class File类 { public static void main(String[]

qq_41028544的博客 183

线程六种开启方式

一.概述 在Java中负责线程的这个功能的是Java.lang.Thread 这个类 特点 线程具备优先级,优先级较高的线程强调cpu的机滤高一些 线程分为用户线程【非守护线程】和守护线程 可以创建线程后自己指定当前线程的优先级以及设定当前线程未守护线程 主方法所在的线程就是主线程,由JVM创建主线程并且在主线程中调用主方法 JVM同时还开启了垃圾回收线程,用户回收堆区的垃圾对象,节约内存资源 多线程的启动 开启多线程的好处 让多个任务看起来像是"同时"执行 为了防止线程阻塞 【网络请求、访问数据库

Apple_Andy的博客 1808

创建线程的三种方式

利用url下载网图的类: package creatthread; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //需要commons-io-2.6 jar包 public class WebPicDownLoad { public void download(String url, String pathName) {

qq_42732184的博客 250

java异步多线程 判断线程状态_判断线程池中的线程是否全部执行完毕

在使用多线程的时候有时候我们会使用 java.util.concurrent.Executors的线程池,当多个线程异步执行的时候,我们往往不好判断是否线程池中所有的子线程都已经执行完毕,但有时候这种判断却很有用,例如我有个方法的功能是往一个文件异步地写入内容,我需要在所有的子线程写入完毕后在文件末尾写“---END---”及关闭文件流等,这个时候我就需要某个标志位可以告诉我是否线程池中所有的子线...

weixin_32139249的博客 1227

7.0、Java多线程——实现Callable接口创建线程

4、创建执行服务ExecutorServiceser=Executor.newFixedThreadPool(1);5、提交执行Futrueresult1=ser.submit(1);6、获取结果booleanr1=result1.get();他实现的是Callable接口,并且重写的是call()方法,然后在通过ser去提交开启线程,开启后不同的地方是会返回一个值。7、关闭服务ser.shutdownNow();2、重写call方法,需要抛出异常。...

m0_52433668的博客 395

python 判断线程是否执行完毕_判断线程池中的线程是否全部执行完毕

在使用多线程的时候有时候我们会使用 java.util.concurrent.Executors的线程池,当多个线程异步执行的时候,我们往往不好判断是否线程池中所有的子线程都已经执行完毕,但有时候这种判断却很有用,例如我有个方法的功能是往一个文件异步地写入内容,我需要在所有的子线程写入完毕后在文件末尾写“---END---”及关闭文件流等,这个时候我就需要某个标志位可以告诉我是否线程池中所有的子线...

weixin_39828783的博客 5976

Java多线程

设计模式(Design pattern)是一套被反复使用,多数人知晓,经过分类编目的,代码设计经验总结。通过对这个设计模式的合理使用能让我们的系统更加健壮yield()yield()

m0_62548870的博客 1021

java线程

java 线程基础 1.创建线程的三种方式 (1)继承Thread 类 (2)实现Runnable接口 (3)实现Callable 接口 2.代码 (1)继承Thread 类,重写run()方法,调用start 开启线程 注意:线程开启不一定立即执行,由cpu 调度执行 package com.example.demo.bean; public class TestThread1 extends Thread{ @Override public void run() {

Small zhou的博客 170
上一篇: Lock和Condition(可重入锁)
下一篇: ReadWriteLock读写锁
刘彦青-Yannis
博客等级 码龄10年 1万+粉丝 234原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘彦青-Yannis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值