Java-进阶:多线程1

目录
一、概述
二、Thread 类
三、创建线程:继承 Thread 类
四、创建线程:实现 Runnable 接口
五、线程优先级
六、线程的生命周期
七、同步代码块


一、概述

1. 进程和线程

  • 进程:进程指正在运行的程序。
  • 线程:线程是进程中的一个执行单元,是程序 使用cpu 的基本单位(调度)。负责当前进程中程序的执行。是进程中单个顺序控制流(执行路径),是一条单独执行的路径
  • 一个程序运行后至少有一个进程,一个进程中可以包含多个线程
  • 在操作系统中,进程是资源分配的基本单位,线程是调度的基本单位。

在没有出现线程之前,进程既是操作系统进行资源分配的基本单位,又是调度的基本单位

  • 单线程程序:若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。程序只有一条执行路径
  • 多线程程序:若有多个任务可以同时执行。程序有多条执行路径

操作系统发展:单道批处理操作——>多道批处理操作系统——>分时操作系统(多进程的)——>线程 
批处理:程序在执行过程中,不会响应用户的请求 
单道批处理操作:一次只能运行一个程序,如果要在计算机运行多个程序,这多个程序,只能一个一个的顺序执行,如果这个正在运行的程序,在运行过程中,执行了一些非常耗时的IO操作(传输数据的过程是没有使用到cpu),这样一来,cpu就闲下来了,但是cpu是计算机中,最为昂贵的 
多道批处理操作系统:同时运行多个程序,显著的提高了cpu的利用率,但是我们一旦一个,程序运行起来,都是是需要使用计算机资源的 
分时操作系统:每一个进程,有一个固定的时间片,在运行一个固定的时间片后,紧接着轮到下一个进程运行(切换)

  • 为什么还会有线程呢? 进程切换的代价太高了,这样一来,每一次进程切换,都需要付出不小的额外的代价,为了减小进程切换的代价,引入了线程,提高CPU的利用率

2. 线程调度

  • 分时调度:所有线程 轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
  • 抢占式调度:优先让 优先级高的 线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度
  • 体现了:程序运行的不确定性

1. CPU 使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
2. 多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

3. 线程控制

  • 线程睡眠static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
//让主线程,休眠3秒
Thread.sleep(3000);
//sleep新的写法
TimeUnit.SECONDS.sleep(3);
System.out.println("睡醒了");
  • 线程加入public final void join():(让当前线程)等待该线程(新加入的线程终止)终止。
SecondThread sec = new SecondThread("zs");
SecondThread thr = new SecondThread("lsii");
SecondThread fou = new SecondThread("wangwu");//启动这三个线程对象
sec.start();
//sec线程上调用join方法 ,让当前线程(main)等待sec线程执行完毕
sec.join();
thr.start();
fou.start();
  • 线程礼让public static void yield():暂停当前正在执行的线程对象,并执行其他线程

只是让当前线程放弃cpu执行权,但是不能阻止它放弃后继续抢夺cpu执行权

  • 后台线程(守护线程)public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出;该方法必须在启动线程前调用。
SecondThread sec = new SecondThread("zs");
SecondThread thr = new SecondThread("lsii");
//将sec和thr线程设置为守护线程
sec.setDaemon(true);
thr.setDaemon(true);
//启动
sec.start();
thr.start();
//之后发现sec和thr是守护线程,就会中断
  • 中断线程public void interrupt():让一个线程控制另外一个线程(有条件的:受阻),可以利用该方法终止另一个线程的运行

1. 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 等阻塞方法处于阻塞状态,它还将收到一个 InterruptedException
2. 中断一个不处于活动状态的线程不需要任何作用。 
中断一个不处于阻塞状态的线程,没有其他任何效果

public class Test {public static void main(String[] args) {FiveThread fiveThread = new FiveThread();fiveThread.start();//在主线程中,终止fiveThread,休眠状态fiveThread.interrupt();}
}// 睡5秒之后,再让该线程输出一句话
class FiveThread extends Thread{@Overridepublic void run() {try {//假如申请了很多的系统资源TimeUnit.SECONDS.sleep(5);System.out.println("FiveThread 睡醒了");} catch (InterruptedException e) {e.printStackTrace();//异常的意义:即使我的线程被异常终止,我也可以保证资源的正常释放}}
}//会抛出 java.lang.InterruptedException: sleep interrupted

4. JAVA程序的运行原理

  • Java命令会启动 JVM,即启动了一个进程,该进程会启动一个主线程,然后主线程调用某个类的 main方法,所以 main方法 都是运行在主线程里
  • jvm 启动后,必然有一个执行路径(线程)从 main方法开始的,一直执行到 main方法结束,这个线程在Java中称之为主线程
  • 当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行
  • 方法在哪个线程中被调用,它就运行在哪个线程中
  • JVM 是一个多线程程序,每个Java 进程都分配一个 JVM 实例
public class ThreadDemo {public static void main(String[] args) {//利用垃圾回收器来证明while(true) {//这里虽然一直在堆空间中,创建数组对象,// 但是始终没有耗尽堆空间,就是因为垃圾回收器,// 在另外一个线程中,帮我们回收垃圾,所以才不会耗尽heap内存// 从而证明,jvm是线程的int[] ints = new int[1024];ints = null;}}
}

二、Thread 类

1. 概述

  • Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程
  • 不是抽象类

2. 构造方法

  • Thread(): 分配新的 Thread 对象
  • Thread(String name):分配新的 Thread 对象,将指定的 name 作为其线程名称

3. 常用方法

  • void start():使该线程开始执行,Java虚拟机调用线程的 run 方法
  • void run():该线程要执行的操作,
  • static void sleep(long millis):在指定毫秒内让当前正在执行的线程休眠,暂停执行
  • static Thread currentThread():返回当前正在执行的线程对象的引用Thread.currentThread()

4. 创建新执行线程的两种方法

  • 将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
  • 声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建 Runnable 的子类对象,传入到某个线程的构造方法中,开启线程。

虽然实现线程有两种方式,其实从客观来讲,线程本身只代表独立的执行路径, 执行的具体内容其实是Task本身,和执行路径的实现本身没有联系;只是我们开发者,想将一个task放在某条独立的执行路径(Thread 类对象,也就是一个线程中)来运行


三、创建线程:继承 Thread 类

  • 创建线程的步骤

  • 定义一个类继承 Thread

  • 重写 run方法

  • 创建子类对象,就是创建线程对象

  • 调用 start 方法,开启线程并让线程执行,同时还会告诉jvm去调用 run 方法

线程对象调用 run方法 不开启线程。仅是对象调用方法。 
线程对象调用 start 开启线程,并让 jvm 调用 run 方法在开启的线程中执行

//测试类
public class Test {public static void main(String[] args) {//创建自定义线程对象MyThread mt = new MyThread("新的线程!");//错误启动线程,这只是普通的方法调用//firstThread.run();//开启新线程mt.start();//再次启动一个线程,会抛异常IllegalThreadStateException //因为一个线程对象只能启动一次,// 如果同一个线程对象,启动多次,就会抛出异常//firstThread.start();//只能创建一个新的对象new MyThread("第二个线程!").start();//获取主线程的名字System.out.println(Thread.currentThread().getName()+":主线程!");}
}//自定义线程
class MyThread extends Thread {//定义指定线程名称的构造方法public MyThread(String name) {//调用父类的String参数的构造方法,指定线程的名称super(name);}//重写run方法,完成该线程执行的逻辑@Overridepublic void run() {//获取线程的名字getName()System.out.println(getName() + ":正在执行!");}
}

四、创建线程:实现 Runnable 接口

1. Runnable 接口的构造方法

  • Thread(Runnable target): 分配新的 Thread 对象,以便将 target 作为其运行对象
  • Thread(Runnable target,String name) : 分配新的 Thread 对象,以便将 target 作为其运行对象;并将指定的 name 作为其名称

2. 创建线程的步骤

  • 定义类实现 Runnable 接口。
  • 覆盖接口中的 run方法
  • 创建 Thread类的 对象
  • 将 Runnable接口 的子类对象作为参数传递给 Thread 类 的构造方法
  • 调用 Thread类的 start() 开启线程。

Thread 类的构造函数:
1. Thread(): 分配新的 Thread 对象
2. Thread(String name):分配新的 Thread 对象,将指定的 name 作为其线程名称

//测试类
public class Test {public static void main(String[] args) {//创建实现 Runnable 接口的子类对象MyRunnable myrunnable = new MyRunnable();//创建Thread实例,在Thread的构造方法中传递Runnable实例//Runnable就代表在 Thread 上运行的任务Thread thread = new Thread(myrunnable);//开启线程thread.start();for (int i = 0; i < 10; i++) {System.out.println("main线程:正在执行!"+i);}}
}//自定义线程执行任务类
class MyRunnable implements Runnable{//定义线程要执行的run方法逻辑//run方法,不能抛出编译时异常//run方法,没有参数没有返回值@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println("我的线程:正在执行!"+i);}}
}

3. 实现 Runnable 接口的原理

为什么需要定一个类去实现Runnable接口呢?继承Thread类和实现Runnable接口有啥区别呢? 
只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务

4. 两种方式的比较

  • 继承 Thread 类方式

  • 如果某个类已经有父类,则无法再继承 Thread 类

  • 实现 Runnable 接口方式

  • 解决了方式一的单继承的局限性

  • 还有一个优点,便于多线程共享数据

第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象;线程分为两部分,一部分线程对象,一部分线程任务。

  • 继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务
  • 实现runnable接口,将线程任务单独分离出来封装成对象类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦

五、线程优先级

1. 概述

  • 我们可以通过 Thread 类中:

  • getPriority 方法 :获取 线程的优先级

  • setPriority 方法 :设置 线程的优先级

2. 线程优先级的范围

  • 如果设置线程优先级的范围,超出了规定范围,会抛出异常;

MAX_PRIORITY 10 //最大优先级 
MIN_PRIORITY 1 //最小优先级 
NORM_PRIORITY 5 //默认优先级

//使用
thread1.setPriority(Thread.MAX_PRIORITY); //设置
thread2.setPriority(Thread.MIN_PRIORITY);
System.out.println(thread1.getPriority());//获取

3. 注意!

  • 对于 TThread 类中的优先级,对于jvm而言,不起决定性作用

也就是说 jvm 在实际调度线程的时候,它使用的优先级可能不仅仅包含我们给每个线程所设置的静态优先级,可能还考虑了其他的很多因素(各个线程的运行状态),所以我们所设置的优先级对于 jvm 只起一个参考作用


六、线程的生命周期

1. 新建状态

  • 创建线程对象

2. 就绪状态

  • žstart() 之后
  • sleep() 睡醒之后
  • yield() 之后
  • 有执行资格,等待cpu调度

3. 运行状态

  • 取得执行权,开始执行

4. 阻塞状态

  • 无执行资格,无执行权

5. 死亡状态

  • 执行完毕,等待垃圾回收

线程的生命周期


七、同步代码块

1. 问题引入——线程安全问题

  • 发生线程安全问题的三个条件

1. 多线程运行环境
2. 多线程访问线程共享数据(存在共享数据)
3. 访问共享数据的操作不是原子操作。 
注:原子操作:不可分割的操作 ,相当于一次性完成的操作

  • 当这三个条件同时满足的时候,才会发生多线程的数据安全问题
  • 解决多线程的线程安全问题:如何实现线程对共享资源的排他性访问(只有我访问完了你们才能修改)?
  • 使用 同步代码块

2. 同步代码块

  • 同步代码块实现 线程同步
  • 线程同步:就是利用锁对象,完成多线程运行环境中,对共享资源的排他性访问(我走你不能走, 你走我不能走)
  • 优点:解决了多线程的安全问题
  • 缺点:消耗资源(当线程很多时,每个线程运行的时候都需要去判断同步锁,这个是很耗费系统资源的)

线程异步:线程之间,互不干扰,各自独立运行(我走我的,你走你的)

  • 格式
synchronized(对象){//critical section对共享资源的访问的代码
}

1. 同步代码块中所使用的对象,称之为锁对象 ——> 锁的角色 
锁对象中,有一个标志位:可以表示两种状态,加锁 和 解锁
2. 持有锁的是线程(一个线程给一个锁对象加锁,我们就说这个线程持有了锁对象)
3. 同步代码块何时给锁对象加锁?进入同步代码块的同时,就给锁对象加锁
4. 线程何时释放持有的锁? 当执行完同步代码块的时候,就会释放同步代码块的锁对象
5. 判断和修改锁对象
标志位
的操作,这是由 jvm 保证一定是原子操作 
6.** 锁对象,究竟是什么对象**? java语言中任意一个对象,都可以充当锁对象的角色(因为任意一个对象中都有一个表示加锁,解锁状态的标志位)

  • 注意点

虽然锁对象可以是任意对象,但是针对同一个(同样的多个共享变量) 的所有操作,都必须保证在同步代码块中,使用同一个锁对象,才能避免线程安全问题。

3. 同步方法

  • 当同步代码块的范围扩大到整个方法的方法体的时候,我们可以将整个方法定义成同步方法在方法声明上加上synchronized
  • 普通的同步代码块: synchronized(锁对象) {}
  • 同步方法: void synchronized 方法名(){}
  • 同步方法的锁对象就是 this

静态方法是否可以定义为同步方法? 
静态的同步方法, 在方法声明上加上static synchronized 
**,都可以充当锁对象的角色(因为任意一个对象中都有一个表示加锁,解锁状态的标志位)

  • 注意点

虽然锁对象可以是任意对象,但是针对同一个(同样的多个共享变量) 的所有操作,都必须保证在同步代码块中,使用同一个锁对象,才能避免线程安全问题。

3. 同步方法

  • 当同步代码块的范围扩大到整个方法的方法体的时候,我们可以将整个方法定义成同步方法在方法声明上加上synchronized
  • 普通的同步代码块: synchronized(锁对象) {}
  • 同步方法: void synchronized 方法名(){}
  • 同步方法的锁对象就是 this

静态方法是否可以定义为同步方法? 
静态的同步方法, 在方法声明上加上static synchronized

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

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

相关文章

Struts2学习笔记(一)--入门常见配置

Struts2框架的学习路线 l 第一天&#xff1a;Struts2的概述、Struts2的入门、Struts2常见的配置、Struts2的Action的编写 l 第二天&#xff1a;Struts2的数据的封装、结果页面配置 l 第三天&#xff1a;Struts2的值栈和OGNL表达式 l 第四天&#xff1a;Struts2的标签库 Struts2…

Java周总结1

一、计算机高级编程语言类型&#xff1a; a.编译型 b.解释型 Hello.java Java源文件 编译 Hello.class 字节码文件 解释 01101100.... &#xff08;二进制&#xff09;机器码指令 computer…

MMKV集成与原理,先收藏了

标准文档流 标准文档流&#xff0c;指的是元素排版布局过程中&#xff0c;元素会默认自动从左往右&#xff0c;从上往下的流式排列方式。前面内容发生了变化&#xff0c;后面的内容位置也会随着发生变化。 HTML就是一种标准文档流文件 HTML中的标准文档流特点通过两种方式体现…

Java周总结3

撰写第三周课程总结及实验报告&#xff08;一&#xff09; Java实验报告 班级 计科一班 学号 20188375 姓名 汤云云 完成时间 评分等级 实验一 Java开发环境与简单Java程序 一、 实验目的 &#xff08;1&#xff09; 熟悉JDK开…

MMKV集成与原理,吊打面试官系列!

前言 校招 -1 年 这个阶段还属于成长期&#xff0c;更需要看重的是你的基础和热情。对于 JS 基础&#xff0c;计算机基础&#xff0c;网络通信&#xff0c;算法等部分的要求会相对高一些。毕竟这个阶段比较难考察你的业务项目中的沉淀&#xff0c;所以只能从基础部分入手考察。…

MMKV集成与原理,成功跳槽阿里!

前言 在初学前端的时候&#xff0c;我们总会遇到一些问题&#xff0c;我们可以在网上看到很多关于前端的这些问题&#xff1a; 你们都是怎么学web前端的&#xff1f; 零基础&#xff0c;怎么自学好前端&#xff1f; 前端需要学多久&#xff0c;都学哪些知识&#xff1f; 想成为…

第四周课程总结试验报告(二)

Java实验报告 实验二 Java简单类与对象 一、 实验目的 &#xff08;1&#xff09; 掌握类的定义&#xff0c;熟悉属性、构造函数、方法的作用&#xff0c;掌握用类作为类型声明变量和方法返回值&#xff1b; &#xff08;2&#xff09; 理解类和对象的区别&#xff0c;掌握构造…

MMKV集成与原理,薪资翻倍

毕业工作一年之后&#xff0c;有了转行的想法&#xff0c;偶然接触到程序员这方面&#xff0c;产生了浓厚且强烈的兴趣&#xff0c;开始学习前端&#xff0c;成功收割了大厂offer&#xff0c;开始了我的程序员生涯。 在自学过程中有过一些小厂的面试经历&#xff0c;也在一些小…

Spring入门与常用配置

什么是Spring Spring&#xff1a;SE/EE开发的一站式框架。 一站式框架&#xff1a;有EE开发的每一层解决方案。 WEB层 &#xff1a;SpringMVC Service层 &#xff1a;Spring的Bean管理&#xff0c;Spring声明式事务 DAO层 &#xff1a;Spring的Jdbc模板&#xff0c;Spring的ORM…

MMKV集成与原理,详细学习指南

前言 本文主要是javascript和css方面的基础面试题&#xff0c;适合面试前以及平时复习食用。 基础知识是前端一面必问的&#xff0c;如果你在基础知识这一块翻车了&#xff0c;就算你框架玩的再6&#xff0c;webpack、git、node学习的再好也无济于事&#xff0c;因为对方就不…

第五周课程总结实验报告(三)

实验三 String类的应用 一、实验目的 &#xff08;1&#xff09; 掌握类String类的使用&#xff1b; &#xff08;2&#xff09; 学会使用JDK帮助文档&#xff1b; 二、实验内容 1.已知字符串&#xff1a;"this is a test of java".按要求执行以下操作&#xff1a;&a…

MMKV集成与原理,赶紧学起来

开头 Web前端开发基础知识学习路线分享&#xff0c;前端开发入门学习三大基础&#xff1a;HTML、CSS、JavaScript。除此之外还要学习数据可视化、Vue、React、Angular相关框架&#xff0c;熟练运用框架提升开发效率&#xff0c;提升稳定性。 [外链图片转存失败,源站可能有防盗…

MMKV集成与原理,轻松拿下offer

从事前端开发工作差不多3年了&#xff0c;自己也从一个什么都不懂的小白积累了一定的理论和实践经验&#xff0c;并且自己也对这3年来的学习实践历程有一个梳理&#xff0c;以供后面来细细回忆品味。 1、为什么选择学习前端开发&#xff1f; 你可能是因为兴趣&#xff0c;完成…

React面试题总结,一文说清!

前言 JavaScript是面向 Web 的编程语言&#xff0c;获得了所有网页浏览器的支持&#xff0c;是目前使用最广泛的脚本编程语言之一&#xff0c;也是网页设计和 Web 应用必须掌握的基本工具。 JavaScript主要用途 嵌入动态文本与HTML页面对浏览器时间做出相应读写HTML元素在数…

React面试题总结,含爱奇艺,小米,腾讯,阿里

前言 校招 -1 年 这个阶段还属于成长期&#xff0c;更需要看重的是你的基础和热情。对于 JS 基础&#xff0c;计算机基础&#xff0c;网络通信&#xff0c;算法等部分的要求会相对高一些。毕竟这个阶段比较难考察你的业务项目中的沉淀&#xff0c;所以只能从基础部分入手考察。…

React面试题总结,就是这么简单

前言 昨天有幸去字节面试了&#xff0c;顺便拿到了offer&#xff0c;把还记得的东西写下来&#xff0c;供大家参考一下。 计算机网络篇 HTTP HTTP 报文结构是怎样的&#xff1f;HTTP有哪些请求方法&#xff1f;GET 和 POST 有什么区别&#xff1f;如何理解 URI&#xff1f;如…

CSS清除默认样式,成功入职腾讯

前言 又逢金三银四&#xff0c;拿到大厂的offer一直是程序员朋友的一个目标&#xff0c;我是如何拿到大厂offer的呢&#xff0c;今天给大家分享我拿到大厂offer的利器&#xff0c;前端核心知识面试宝典&#xff0c;内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题\数…

CSS清除默认样式,技术详细介绍

前言 JavaScript是面向 Web 的编程语言&#xff0c;获得了所有网页浏览器的支持&#xff0c;是目前使用最广泛的脚本编程语言之一&#xff0c;也是网页设计和 Web 应用必须掌握的基本工具。 JavaScript主要用途 嵌入动态文本与HTML页面对浏览器时间做出相应读写HTML元素在数…

CSS清除默认样式,看完这篇彻底明白了

前端的兴起 前端真正兴起和开始频繁出现在大家的视线里&#xff0c;大概是在十年前。彼时的 Web 开发基本是由后端主导&#xff0c;前端能做的只是校验一下数据、操作一下 DOM。&#xff08;其中数据检验是 JS 产生的根本原因&#xff1a;当时网络太慢&#xff0c;在服务端检验…

CSS清除默认样式,经典好文

前言 不要为了面试而去背题&#xff0c;匆匆忙忙的&#xff0c;不仅学不进去&#xff0c;背完了几天后马上会忘记。 你可能会说&#xff0c;“没办法&#xff0c;这不是为了能找份工作嘛&#xff01;”。我想说的是&#xff0c;“那你没开始找工作的时候&#xff0c;咋不好好…