【java笔记】java多线程

目录

一、概念

1.1 什么是进程?

1.2 什么是线程?

1.3 什么事多线程?

1.4 进程和线程的关系

二、线程对象的生命周期

三、实现线程有两种方式 

3.1 继承 java.lang.Thread,重写 run方法

3.2 实现 java.lang.Runnable 接口,实现run方法

四、Thread和Runnable的区别

五、线程状态及其状态转换

5.1 新建状态(New)

5.2 就绪状态(Runnable)

5.3 运行状态(Running)

5.4 阻塞状态(Blocked)

5.5 死亡状态(Dead)

六、线程调度

6.1 常见的线程调度模型有哪些?

 6.2 调整线程优先级

6.3 线程睡眠  

6.4 线程等待

6.5 线程让步

6.6 线程加入

6.7 线程唤醒

七、线程同步 

八、线程数据传递 

8.1 通过构造方法传递数据 

8.2 通过变量和方法传递数据 

8.3 通过回调函数传递数据 


一、概念

1.1 什么是进程?

进程是执行程序的一次执行过程。在一个操作系统中,每一个独立执行的程序都可以称之为一个进程,也就是“正在运行的程序”。它是一个动态概念,是系统资源分配的单位。例如:QQ,播放器,游戏等。

1.2 什么是线程?

线程就是指的是进程中的实际运行单位,它是操作系统中进行运算调度的最小单位。换句话说,线程是进程中的一个最小运行单位。每个运行的程序都是一个进程,在一个进程中还可以有多个执行单元同时运行,这些执行单元可以看做程序执行的一条条线索,被称为线程。一个进程中至少有一个线程,不然没有存在的意义。线程就是CPU调度和执行的单位。例如:你在看视频(进程)的同时可以听到声音(线程),看到图像(线程)和字幕(线程)等。

1.3 什么事多线程?

多线程就是指的是一个进程中同时有多个执行路径即线程在执行。

1.4 进程和线程的关系

进程和线程是包含关系。及一个进程可以有多个线程。

二、线程对象的生命周期

  • 新建状态
  • 就绪状态
  • 运行状态
  • 阻塞状态
  • 死亡状态

三、实现线程有两种方式 

3.1 继承 java.lang.Thread重写 run方法

// 定义线程类
public class MyThread extends Thread{
	public void run(){
	
	}
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();

调用run()方法内存图

调用start()方法内存图:

注意:

t.run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)

t.start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
 

3.2 实现 java.lang.Runnable 接口,实现run方法

// 定义一个可运行的类
public class MyRunnable implements Runnable {
	public void run(){
	
	}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();

注意:
第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

四、Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去处理同一个资源
  2. 可以避免java中的单继承的限制
  3. 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  4. 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM就是在操作系统中启动了一个进程。
 

五、线程状态及其状态转换

5.1 新建状态(New)

新创建了一个线程对象。

5.2 就绪状态(Runnable)

线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

5.3 运行状态(Running)

就绪状态的线程获取了CPU,执行程序代码。

5.4 阻塞状态(Blocked)

阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  • 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
  • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5.5 死亡状态(Dead)

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。


六、线程调度

6.1 常见的线程调度模型有哪些?

  • 抢占式调度模型:
    那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
    java采用的就是抢占式调度模型

  • 均分式调度模型:
    平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
    平均分配,一切平等。
    有一些编程语言,线程调度模型采用的是这种方式。

 6.2 调整线程优先级

Java线程有优先级,优先级高的线程会获得较多的运行机会。Java线程的优先级用整数表示,取值范围是1~10,设置方法如下:

方法名作用
int getPriority()获得线程优先级
void setPriority(int newPriority)设置线程优先级

6.3 线程睡眠  

Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
 

6.4 线程等待

Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
 

6.5 线程让步

Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
 

6.6 线程加入

join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
 

6.7 线程唤醒

Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

七、线程同步 

synchronized有三种写法:

第一种:同步代码块
灵活

synchronized(线程共享对象){
    同步代码块;
}
第二种:在实例方法上使用synchronized
表示共享对象一定是 this 并且同步代码块是整个方法体。

第三种:在静态方法上使用synchronized
表示找 类锁。类锁永远只有1把。

就算创建了100个对象,那类锁也只有1把。
 

总结一下:

1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
 

八、线程数据传递 

在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下,数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。

8.1 通过构造方法传递数据 

在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据: 

package mythread; 
public class MyThread1 extends Thread 
{ 
    private String name; 
    public MyThread1(String name) 
    { 
        this.name = name; 
    } 
    public void run() 
    { 
        System.out.println("hello " + name); 
    } 
    public static void main(String[] args) 
    { 
        Thread thread = new MyThread1("world"); 
        thread.start(); 
    } 
} 

由于这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。由于Java没有默认参数,要想实现类似默认参数的效果,就得使用重载,这样不但使构造方法本身过于复杂,又会使构造方法在数量上大增。因此,要想避免这种情况,就得通过类方法或类变量来传递数据。 


8.2 通过变量和方法传递数据 

向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。下面的代码是对MyThread1类的改版,使用了一个setName方法来设置 name变量: 

package mythread; 
public class MyThread2 implements Runnable 
{ 
    private String name; 
    public void setName(String name) 
    { 
        this.name = name; 
    } 
    public void run() 
    { 
        System.out.println("hello " + name); 
    } 
    public static void main(String[] args) 
    { 
        MyThread2 myThread = new MyThread2(); 
        myThread.setName("world"); 
        Thread thread = new Thread(myThread); 
        thread.start(); 
    } 
} 

8.3 通过回调函数传递数据 

上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,如在下面代码的run方法中产生了3个随机数,然后通过Work类的process方法求这三个随机数的和,并通过Data类的value将结果返回。从这个例子可以看出,在返回value之前,必须要得到三个随机数。也就是说,这个 value是无法事先就传入线程类的。 

package mythread; 
class Data 
{ 
    public int value = 0; 
} 
class Work 
{ 
    public void process(Data data, Integer numbers) 
    { 
        for (int n : numbers) 
        { 
            data.value += n; 
        } 
    } 
} 
public class MyThread3 extends Thread 
{ 
    private Work work; 
    public MyThread3(Work work) 
    { 
        this.work = work; 
    } 
    public void run() 
    { 
        java.util.Random random = new java.util.Random(); 
        Data data = new Data(); 
        int n1 = random.nextInt(1000); 
        int n2 = random.nextInt(2000); 
        int n3 = random.nextInt(3000); 
        work.process(data, n1, n2, n3); // 使用回调函数 

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

图像描述算法排位赛:SceneXplain 与 MiniGPT4 谁将夺得桂冠?

如果你对图像描述算法的未来感到好奇,本场“图像描述算法排位赛”绝对是你不能错过的!在这场较量中,SceneXplain 和 MiniGPT-4 将会比试,谁将摘得这场比赛的桂冠? 背景介绍 在上篇文章中,我们介绍了图像描述…

【UE】倒计时归零时结束游戏

上一篇博客(【UE】一个简易的游戏计时器)完成了游戏时间每秒1的功能,本篇博客在此基础上完成倒计归零时结束游戏的功能 效果 步骤 1. 打开“ThirdPersonGameMode”,将剩余的秒数和分钟数的默认值分别设置为1和59 在事件图表中添…

Java设计模式-day02

4,创建型模式 4.2 工厂模式 4.2.1 概述 需求:设计一个咖啡店点餐系统。 设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设…

Linux: 进程间通信机制

文章目录 1. 前言2. 进程间通信机制2.1 管道2.1.1 匿名管道2.1.2 popen() 和 pclose()2.1.3 命名管道 FIFO 2.2 消息队列2.3 共享内存2.4 信号量2.5 网络套接字2.6 UNIX套接字2.7 信号 3. 参考资料 1. 前言 限于作者能力水平,本文可能存在谬误,因此而给…

Javaee Spring的AOP简介

一.Spring的AOP简介 1.1 什么是AOP AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代 理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续,是软件开发中的一个热点,也是…

shell脚本控制

shell脚本编程系列 处理信号 Linux利用信号与系统中的进程进行通信,通过对脚本进行编程,使其在收到特定信号时执行某些命令,从而控制shell脚本的操作。 Linux信号 shell脚本编程会遇到的最常见的Linux系统信号如下表所示: 在默…

【获奖案例巡展】信创先锋之星——浙江省某市区视频能力中心

为表彰使用大数据、人工智能等基础软件为企业、行业或世界做出杰出贡献和巨大创新的标杆项目,星环科技自2021年推出了“新科技 星力量” 星环科技科技实践案例评选活动,旨在为各行业提供更多的优秀产品案例,彰显技术改变世界的力量&#xff0…

Cycling 74 Max for Mac:音乐可视化编程软件

Cycling 74 Max是一款音乐、视觉、互动艺术等领域中广泛使用的编程语言和应用软件,它允许用户创作和控制实时音频和视频效果、交互式应用程序和媒体艺术品等。 Max将程序设计和可视化编程相结合,通过简单的拖拽和连接方式,用户可以将各种功能…

基于springboot的大学生租房系统源码论文数据库

3.1系统功能 现在无论是在PC上还是在手机上,相信全国所有地方都在进行大学生租房管理。随着经济的不断发展,系统管理也在不断增多,大学生租房系统就是其中一种,很多人会登录到相关的租房系统查看租房信息,还能查看房屋…

微信小程序开发--利用和风天气API实现天气预报小程序

本来是参照《微信小程序开发实战》做一个天气预报小程序的,实际运行的时候提示错误,code 400,参数错误。说明问题应该出在查询API的语句上,没有返回结果。 查阅后才知道,可能书籍出版时间较早,现在的和风获…

类对象

一、类初识 类:表示一种事物所具有的共同特征和行为 对象:一个类的实例 如下图,通过狗这个类进行详解 这是一个Dog类 对象:斗牛犬、小猎犬、牧羊犬 类中的属性:breed(品种)、size(大小)、color(颜色)、age(年龄)、 …

安全常见基础名词概念

一、域名 1、域名:相当网站的名字,互联网上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位。 2、网域名系统(Domain Name System)有时也简称为域名(DNS),是互…

探索【Stable-Diffusion WEBUI】的插件:骨骼姿态(OpenPose)

文章目录 (零)前言(一)骨骼姿态(OpenPose)系列插件(二)插件:PoseX(三)插件:Depth Lib(四)插件:3D …

响应式开发(HTML5CSS3)实现媒体查询的功能案例

目录 前言 一、媒体查询知识点 二、实现功能的尺寸 三、代码部分 1.不带嵌套的媒体查询功能 1.1.代码段 1.2.运行结果 2.带嵌套的媒体查询功能 2.1.代码段 2.2.运行结果 2.2.3视频效果 前言 1.本文讲解的响应式开发技术(HTML5CSS3Bootstrap&#xff09…

Auto-GPT免费尝鲜之初体验-使用攻略和总结

Auto-GPT免费尝鲜之初体验-使用攻略和总结 写在前面的废话一、部署 Auto-GPT二、试运行 Auto-GPT三、我踩过的坑四、后续探索 写在前面的废话 ChatGPT 的交互模式,是和一个 “人” 对话聊天。 如果你想了解更多ChatGPT和AI绘画的相关知识,请参考&#…

ArcGIS Pro用户界面

目录 1 功能区 1.1 快速访问工具栏 1.2 自定义快速访问工具栏 1.3 自定义功能区选项 1.3.1 添加组和命令 1.3.2 添加新选项卡 2 视图 3 用户界面排列 ​编辑 4 窗格 4.1 内容窗格 4.2 目录窗格 4.3 目录视图(类似ArcCatalog) 4.4 浏览对话框…

python:面向对象编程(知识点+代码)

文章目录 一、类和对象1、对象属性的默认值设置2、对象属性的添加、修改与删除3 、类属性 二、类的继承 引言:面向对象编程时一门编程语言重要的功能,我们之前所学的 c,java都为面向对象编程语言,这里给大家拓展一下,…

什么是CDN加速?CDN加速有哪些作用?

一、什么是 CDN CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 是在现有 Internet 基础上增加一层新的网络架构,通过部署边缘服务器,采用负载均衡、内容分发、调度等功能,使用户可以就近访问获取所需内容&#xff…

多维时序 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络多变量时间序列预测

多维时序 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络多变量时间序列预测 目录 多维时序 | MATLAB实现BO-CNN-LSTM贝叶斯优化卷积神经网络-长短期记忆网络多变量时间序列预测效果一览基本介绍模型搭建程序设计参考资料 效果一览 基本介绍 MATLAB实现BO-CNN-…

2023年报考CSM敏捷教练认证好不好?含金量高吗?

CSM,Certified Scrum Master,是Scrum联盟发起的Scrum认证。帮助个人从自身、团队和组织层面,学习技能和工具来扩展实践的层面,帮助团队正确使用Scrum,从而提高项目整体成功的可能性。 认证收益 职业能力提升 推动企业…