【java笔记】java多线程

news2024/11/14 20:46:39

目录

一、概念

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(); 
    } 
} 

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

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

相关文章

材料写作素材:关于“大”排比句40例

1.一轮思想政治“大督查”,一轮政策落实“大检查”,一次非公企业“大走访”,一次问题线索“大起底”,一批典型案例“大曝光”。 2.在重大风险挑战面前豁得出去、顶得上去,在重大困难考验面前迎难而上、敢于胜利&#…

【计网】WebSocket协议

目录 一、背景 二、WebSocket握手过程 三、SpringBoot中使用WebSocket协议 1、服务器 2、客户端 一、背景 一般的web开发以请求响应为主即客户端发送一个请求,服务器返回一个响应,这就使得类似聊天等需求基于HTTP协议进行实现时比较消费资源&#xf…

大数据之Hadoop分布式文件系统HDFS

目录: 一、介绍二、HDFS 设计原理三、原理图形介绍四、HDFS 常用 shell 命令五、HDFS相关JavaAPI 一、介绍 HDFS (Hadoop Distributed File System)是 Hadoop 下的分布式文件系统,具有高容错、高吞吐量等特性,可以部署…

厨电新十年,不可逆的行业分化与老板电器的数字进化

“人生就像滚雪球,最重要之事是发现湿雪和长长的山坡。”股神巴菲特的这句名言,让坡是否长、雪是否厚成为人们评价一个行业、一家公司的标准之一。 家电行业,厨电曾是最后一块“坡长雪厚”之地,投资者也对相关企业给出了相当的热…

FIR半带滤波器

FIR半带滤波器 半带滤波器原理: CIC滤波器是一种适合于工作在高采样率条件下的滤波器。 半带滤波器是一种非常适合于2倍抽取的FIR滤波器。 半带滤波器可以使2倍抽取的每秒乘法次数比一般线性相位的FIR滤波器减少近1/2。 半带滤波器是一种实现数字下变频的高效数…

暗藏的比特币白皮书已删除 苹果其实与Web3“格格不入”?

据悉,Apple已从其最新的Mac OS Ventura beta中删除了比特币白皮书,虽然该公司从未对白皮书的存在提供任何官方解释,但许多人猜测这是对加密货币日益增长的重要性的认可。 4月上旬,科技专家Andy Baio偶然发现,自己的Mac…

浅述 国产仪器 1652AM任意波形发生器

1652AM任意波形发生器是一款多通道、多功能的任意波形发生器。它在兼顾了输出信号高质量的同时,实现了高通道密度。它可与其它通用或专用模块化测试仪器构成综合测试系统或平台,支持众多解决方案,包括量子计算机调控信号生成、大规模MIMO信号…

活动目录(Active Directory)安全审计

延迟响应变化的影响可能会使原本应该微不足道的颠簸滚雪球变成无法弥补的损害。这在 Windows Active Directory 环境中更为重要,因为这种延迟造成的损害可能会使组织损失数百万美元!在这种情况下,需要一个警惕的警报系统,该系统可…

WPF学习

一、了解WPF的框架结构 (第一小节随便看下就可以,简单练习就行) 1、新建WPF项目 xmlns:XML的命名空间 Margin外边距:左上右下 HorizontalAlignment:水平位置 VerticalAlignment:垂直位置 2…

性能测试开始前的需求调研

之前的博客聊聊性能测试开始前的准备工作,聊了一些关于性能测试开始前要做的准备工作。这篇博客,来谈谈性能测试开始前的需求调研阶段,我们要做什么,关注那些Point。。。 一、基本信息 信息类型说明项目名称项目归属的业务线&am…

低代码应用开发平台,让数据管理更简单!

在数据管理越来越规范化的今天,低代码应用开发平台也获得了进步和发展的机会和空间。想要将企业内部的数据资源做好系统管理,从而为各个时期的经营决策提供强有力的证据和基础,低代码应用开发平台就是其中备受喜爱的工具。本文主要从它的特点…

重磅!阿里云云原生合作伙伴计划全新升级:加码核心权益,与伙伴共赢新未来

在今天的 2023 阿里云合作伙伴大会上,阿里云智能云原生应用平台运营&生态业务负责人王荣刚宣布: “阿里云云原生合作伙伴计划”全新升级。他表示: 云原生致力于帮助企业客户最大限度的减轻运维工作,更好的实现敏捷创新&#x…

MQTT 协议

MQTT 简介 MQTT是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点得它对很多场景来说都是很有的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网…

RAC集群节点2异常时节点1的database实例无法提供服务问题的分析

在客户的数据库RAC集群环境中,节点2发生了异常,最终通过重启解决。在节点2发生异常的10分钟左右时间内,由于RAC集群节点2异常,此时节点1的database实例无法提供服务问题,程序操作报超时; 对此现象&#xf…

【Linux】-关于Linux的指令(中)

作者:小树苗渴望变成参天大树 作者宣言:认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 文章目录 前言一、man指令二、cp指令三、mv指令四、cat指令五、more和less指令六、hea…

【力扣-141】 环形链表 + 【力扣-142】 环形链表 II

🖊作者 : Djx_hmbb 📘专栏 : 数据结构 😆今日分享 : 霍桑效应(霍索恩效应) : 是指那些意识到自己正在被别人观察的个人具有改变自己行为的倾向。 霍桑效应告诉我们:从旁人的角度,善意的谎言和夸奖真的可以造就一个人&a…

线程的生命周期以及sleep()方法和wait()方法

三种休眠状态:Blocked,Waiting,Timed_Waiting 注意两个Blocked态是不一样的,上面的Blocked只要睡眠时间到了马上进入运行态,下面处于Blocked的线程还需要抢到锁才能进入运行态 sleep()和wait()方法: sleep…

【翻译一下官方文档】创建uniCloud服务空间并关联新建的uniapp项目

我将用图文的形式,把市面上优质的课程加以自己的理解,详细的把:创建一个uniCloud的应用,其中的每一步记录出来,方便大家写项目中,做到哪一步不会了,可以轻松翻看文章进行查阅。(此文…

论文笔记:An Interactive-Voting Based Map Matching Algorithm

2010 MDM 1 ST-matching的问题 论文笔记:Map-Matching for low-sampling-rate GPS trajectories(ST-matching)_UQI-LIUWJ的博客-CSDN博客 当轨迹很长,且车辆通过多线平行的道路时,ST-Matching的效果较差&#xff0c…

冯·诺依曼体系结构与初始操作系统

目录 冯诺依曼体系结构 冯诺依曼体系结构图 内存 外存 网卡和磁盘 结构之间运算速度的差异 缓冲区 初始操作系统 概念 操作系统上边与下边分别有什么 从上到下依次顺序解析 用户 用户操作接口 系统调用接口 操作系统四项管理 驱动 硬件 冯诺依曼体系结构 冯诺…