当前位置: 首页 > news >正文

Java多线程编程

线程基础知识

进程:运行中的程序,进程启动时操作系统会为其分配内存空间

进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程

一个进程可以有多个线程,线程由进程创建,是进程的一个实体,是CPU调度的基本单位


单线程:同一时刻只允许执行一个线程

多线程:同一时刻可执行多个线程


并行:在一段时间内,多个任务同时执行

并发:在同一时刻,多个任务同时运行

并行和并发可以同时存在,宏观微观。

Runtime runtime = Runtime.getRuntime(); //获取当前电脑的cpu数量/核心数 eg:八核处理器 int cpuNums = runtime.availableProcessors(); System.out.println("当前有cpu 个数="+ cpuNums);

线程的使用方法

继承Thread类,重写run方法

终端输入jConsole可监控线程运行

package Thread; public class Demo01 { // 1)请编写程序,开启一个线程,该线程每隔1秒。在控制台输出“喵喵,我是小猫咪” // 2)对上题改进:当输出80次喵喵,我是小猫咪,结束该线程 public static void main(String[] args) { Cat cat = new Cat(); cat.start(); //主线程启动子线程,主线程不会阻塞会继续执行 //主线程和子线程交替执行 for (int i = 1; i < 100; i++) { System.out.println("主线程工作中:" + i); try { Thread.sleep(1000);//线程休眠1秒 } catch (InterruptedException e) { throw new RuntimeException(e); } } //主线程执行完了如果子线程还没执行完,主线程会等子线程执行完 //当所有线程执行完毕进程才会退出 } } class Cat extends Thread { @Override //重写线程的run方法 public void run() { int time = 0; while (true) { System.out.println("想打游戏" + ++time + "次\t"+Thread.currentThread().getName());//获取当前执行的线程名 try { Cat.sleep(1000);//线程休眠1秒 } catch (InterruptedException e) { throw new RuntimeException(e); } if (time == 80) { break; } } } }

为什么启动线程用star?

用上述代码举例

若使用cat.run则只是单纯调用了run方法而没有启动线程,只有run方法执行完才会向下运行

源码.底层机制

(1)
public synchronized void start() {

start0();
(2)
//start0()是本地方法,是JVM调用,底层是c/c++实现

//真正实现多线程的效果, 是start0(),而不是 run

private native void start0();


实现Runnable接口,重写run方法

由于一个类只能有一个父类,当类已经拥有继承关系仍想调用线程时,可通过实现runnable接口来进行调用。采用了设计模式——静态代理模式

package Thread; public class Demo02 { public static void main(String[] args) { Student student = new Student(); //student.start();此处不能调用start方法 Thread t = new Thread(student);//创建线程对象,把student对象(实现Runnable)赋值给t t.start(); } } class Person{} class Student extends Person implements Runnable{ @Override public void run() { System.out.println("我是甜菜"); } }

继承Thread vs 实现Runnable的区别

从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口,本质都是调用start0{};

实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable。

Student student = new Student(); Thread t = new Thread(student); Thread t1 = new Thread(student); t.start(); t1.start();

多线程售票问题

无论是使用继承Thread还是实现Runnable都会出现超售现象

package Thread; public class Demo03 { public static void main(String[] args) { sellTicket st = new sellTicket(); new Thread(st).start(); new Thread(st).start(); new Thread(st).start(); } } class sellTicket implements Runnable{ private int ticket = 10; public void run(){ while(true){ System.out.println(Thread.currentThread().getName()+"卖出了一张票\t剩余"+ticket--+"张票"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } if(ticket==0){ System.out.println("票已售完"); break; } } } }

通知线程退出

加入控制变量来控制线程的运行

package Thread; public class Demo04 { public static void main(String[] args) throws InterruptedException { Test test = new Test(); new Thread(test).start(); Thread.sleep(5000); test.stop();//调用stop方法控制线程的运行 } } class Test implements Runnable{ int time=0; boolean loop = true;//定义变量来控制线程是否继续运行 @Override public void run(){ while(loop){ System.out.println("线程启动了"+ ++time); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public void stop(){ loop = false; } }

线程的常用方法

setName //设置线程名称,使之与参数name 相同 getName//返回该线程的名称 start//使该线程开始执行;Java 虚拟机底层调用该线程的start0方法//调用线程对象run 方法; runsetPriority//更改线程的优先级 getPriority//获取线程的优先级 sleep//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) interrupt //中断线程

线程优先级范围

MIN_PRIORITY=1;

NORM_PRIORITY=5;

MAX_PRIORITY=10;

sleep是线程的静态方法,使当前线程休眠

线程中断interrupt并没有真正的结束线程,一般用于中断正在休眠的线程

package Thread; public class Demo05 { public static void main(String[] args) throws InterruptedException { MyThread t1 = new MyThread(); t1.setName("线程1"); t1.start(); for (int i = 0; i < 5; i++){ Thread.sleep(1000); System.out.println("hello"+ ++i); } t1.interrupt(); } } class MyThread extends Thread{ @Override public void run(){ while(true) { for (int j = 0; j < 30; j++) System.out.println(getName() + "正在运行" + ++j); try { System.out.println(getName() + "休眠中..."); Thread.sleep(20000); } catch (InterruptedException e) { System.out.println( getName() + "被中断"); } } } }

yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

package Thread; //案例:main线程创建一个子线程,每隔1s输出hello,输出10次,主线程每隔1秒,输出hi,输出10次. // 要求:两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续, public class Demo06 { public static void main(String[] args) throws InterruptedException { T t = new T(); t.start(); for (int i = 0; i < 10; i++) { System.out.println("hi"+i); Thread.sleep(1000); if(i==5){ System.out.println("主线程让子线程join插队"); t.join();//子线程执行完再回来执行主线程 //System.out.println("主线程让行子线程yield"); //t.yield();//让出cpu执行权,但结果依然是子线程主线程交替执行 } } } } class T extends Thread{ @Override public void run(){ for (int i = 0; i < 10; i++) { System.out.println("hello"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }

用户线程和守护线程

用户线程:也叫工作线程,当线程的任务执行完或通知方式结束

守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

常见的守护线程:垃圾回收机制

package Thread; public class Demo07 { public static void main(String[] args) throws InterruptedException { T2 t2 = new T2(); Thread t = new Thread(t2); t.setDaemon(true) ; // 设置为守护线程 t.start(); // 启动该守护线程,当主线程(此处为用户线程)运行结束,守护线程也会结束 for (int i = 0; i < 10; i++){ System.out.println("小王正在努力敲代码"); Thread.sleep(1000); } } } class T2 implements Runnable{ @Override public void run() { for (; ; ) { System.out.println("小明正在努力敲代码"); try { Thread.sleep(1000); //此处只能用try catch,不能用throws //由于子类方法不能抛出比父类方法更宽泛(或更多)的受检异常 //父类 run() 没有声明抛出任何受检异常,所以子类重写时也不能添加 throws InterruptedException } catch (InterruptedException e) { throw new RuntimeException(e); } } } }

线程生命周期

NEW(新建)

RUNNBALE(可运行)

BLOCKED(阻塞)

WAITING(无限等待)

TIMED_WAITING(限时等待)

TERMINATED(终止)


线程同步机制

在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作.

同步具体方法-Synchronized

同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码

//需要被同步代码;}
synchronized还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m (String name){

//需要被同步的代码}

package Thread; public class Demo08 { public static void main(String[] args) { sellTicketPro st = new sellTicketPro(); new Thread(st).start(); new Thread(st).start(); new Thread(st).start(); } } class sellTicketPro implements Runnable{ private int ticket = 100; private boolean loop = true; public void run(){ while(loop){ sell(); } } public static void m() {//静态方法实现同步代码块,锁在类对象,类. class synchronized (sellTicketPro.class){ System.out.println("m方法开始执行"); } } public /*synchronized*/ void sell(){//同步方法,锁在this对象 synchronized (this){//同步代码块 if(ticket<=0){ System.out.println("票已售完"); loop = false; return; } System.out.println(Thread.currentThread().getName()+"卖出了一张票\t剩余"+--ticket +"张票");} try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } }

互斥锁

Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。

关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问

同步的局限性:导致程序的执行效率要降低

同步方法(非静态的没有使用static修饰)的锁可以是this,也可以是其他对象(要求是同一个对象)

如果方法使用static修饰,默认锁对象:当前类.class

实现的落地步骤:

需要先分析上锁的代码

选择同步代码块或同步方法

要求多个线程的锁对象为同一个即可!

线程的死锁


多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生.

释放锁

当前线程的同步方法、同步代码块执行结束

当前线程在同步代码块、同步方法中遇到break、return。

当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束

当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

下面操作不会释放锁
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起该线程不会释放锁。提示:应尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用

http://www.jsqmd.com/news/532905/

相关文章:

  • HunyuanVideo-Foley镜像特性:内置FFmpeg支持AI音效转MP3/AAC/OGG
  • Wan2.2-I2V-A14B效果展示:同一prompt下WebUI vs API生成结果一致性验证
  • 【由浅入深探究langchain】第九集-简单的Agent工具调用
  • 搭建ai大模型集群
  • 使用IDEA插件提升效率:在开发环境中直接调用MiniCPM-o-4.5
  • 如何构建Headplane现代化Web管理界面:容器化部署终极指南
  • Granite TimeSeries FlowState R1开发环境配置全攻略:从零开始搭建AI预测项目
  • 头皮精华成分分析:哪种成分最适合你的头皮问题? - 博客万
  • 三. Java特性、版本、JDK/JRE/JVM
  • OpenClaw技能开发套件:为Qwen3.5-4B-Claude定制专属工具
  • 2026年3月,探寻市面上好的喷雾干燥机厂家,流化床干燥机/单锥干燥机/耙式干燥机/闪蒸干燥机,喷雾干燥机批发厂家分析 - 品牌推荐师
  • 毕业设计:基于springAi+vue的非遗数字文化馆(源码)
  • 众智商学院:助力外审员备考与能力进阶 - 众智商学院官方
  • 重构前端路由开发范式:vite-plugin-pages 从原理到实践
  • Avalonia+VSCode实时预览开发指南:用ReactiveUI快速构建响应式桌面应用(含项目结构解析)
  • FLUX.1-dev像素工坊应用场景:像素化无障碍界面设计——高对比度UI生成
  • 起猛了,发现我的工位上坐着一只“硅基吸血鬼”!
  • 10分钟快速掌握QQ空间历史说说备份神器
  • 分析金丝绒瓷砖生产商,莱曼缔克在泉州、佛山、肇庆的口碑如何? - 工业设备
  • 防脱精华品牌对比:日系、欧美、国货三足鼎立大PK - 博客万
  • TranslucentTB:为Windows任务栏注入透明美学的轻量级神器
  • ssm+java2026年毕设数据学院工作量管理系统【源码+论文】
  • 2026年杭州吸塑板材口碑榜,瑞新吸塑板材专业度咋样 - 工业品网
  • Anything V5效果展示:一键生成高质量二次元角色肖像图
  • C#读写各类配置文件
  • Leather Dress Collection 惊艳效果展示:多风格时尚文案与设计描述生成
  • Elden Ring帧率解锁终极指南:突破60帧限制的完整教程
  • CRM系统评测:如何避开功能缺陷,选择全球化支撑能力强的AI平台? - 纷享销客智能型CRM
  • 书匠策AI:问卷设计的“古法匠心”与“智法革新”大碰撞
  • hadoop+spark+hive链家租房租房推荐系统 租房可视化 K-means聚类算法 线性回归预测算法 机器学习