javaEE初阶————多线程初阶(1)

ops/2025/2/9 4:57:52/

多线程初阶————

1,认识线程

1.1 概念

1)线程是什么

线程就是一个“执行流”,可以理解为程序执行的最小单位;

可以看成轻量级的进程;

2)为啥要有线程

“并发编程” 的需要,但是我们不是已经有进程了吗,我们要知道,我们进行的是服务器开发,我们在访问网站的时候,一个用户进行访问就是一个进程,用户的数据量是非常庞大的,进程的创建和销毁需要的开销就会变得非常非常大,这样我们就引出了线程,让一个进程中包含一个或多个进程,提升效率,

3)线程和进程的区别

1,进程是操作系统进行资源分配的基本单位,线程是操作系统进行运算调度的基本单位;

2,线程的创建,销毁,调度需要的开销更小;

3,进程之间互不影响,同一进程下的线程会互相影响,创建进程后会自动创建一个线程,第一个线程会涉及到申请资源的操作,其余线程不会涉及,进程销毁才会释放资源,线程的销毁不会释放资源;

4,因为线程是调度相关,所以每一份线程都有调度相关的数据

5,一个进程死掉了不会影响其他进程,但是一个进程中一个线程死掉了就掀桌了,全部都运行不了了;

4)java中线程和操作系统的关系

java中包装好了操作系统中对线程操作的API,但是java是不推荐多进程编程的,我们只去学习多线程编程;

1.2 第一个多线程程序

java">class MyThread extends Thread{public void run(){System.out.println("myThread");}
}
public class Demo1 {public static void main(String[] args) {Thread thread = new MyThread();thread.start();System.out.println("main");}
}

 main就是进程刚创建我们自动生成的第一个线程,运行

只是个示范,看接下来的讲解就好;

1.3 创建线程

1)创建对象继承Thread类
java">class MyThread2 extends Thread{public void run(){while(true){System.out.println("MyThread2 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
public class Demo2 {public static void main(String[] args) {Thread thread = new MyThread2();System.out.println("主线程");}
}

我们创建一个类,让它继承Thread类,Thread类中给我们提供了一个run方法,让我们自己去写里面的内容,我们就在自己实现的类中重写run方法,我们循环打印,并且打印一次睡眠1秒,在主线程也就是main方法中,打印主线程,我们来看运行结果;

 

只有一个主线程,因为我们没有去调度线程,我们可以直接用.run或者是.start来开启线程

java">thread.run();

程序一直在运行,这里不明显,我们来借助一个工具,

找到jdk中的bin ,

以管理员身份运行它

找到我们刚才创建的Demo2

点击线程

我们看到main线程一直在等待,因为我们使用的run方法,所以是在主线程上运行的,如果我们想看到我们自己创建的线程,就要用start

我们在试试;

这个Thread——0就是我们自己创建的线程,但是main呢,还有为啥先打印的主线程呢,因为调度随机的,我们不知道操作系统让拿个线程先执行,所以就会发生这样的状况,这也是我们后期要重点掌握的,要怎么保证线程之间协调配合,避免乌鸦哥掀桌,哈哈哈,main线程在这里已经结束了,没啥好说的了,下一个; 

2)实现Runnable接口
java">class MyRunnable implements Runnable{public void run(){while(true){System.out.println("MyRunnable 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo3 {public static void main(String[] args) {Thread thread = new Thread(new MyRunnable());thread.start();System.out.println("主线程");}
}

这是第二种,我们Runnable接口,我们还是要使用Thread类来创建,这不有病吗.........这么麻烦,还不如用第一种,其实这种想法是不对的,大家听,没听过,高内聚低耦合,这里就谈到了低耦合,我们使用接口,在想要修改的时候去修改接口的代码即可,是不影响Thread的,但是我们使用Thread的时候,想要修改的时候,就要修改Thread中的代码, 可能扯到线程相关的代码,而且用类继承一次局限性大,接口更灵活;

运行

我们这次让主线程也活着

java">
class MyRunnable implements Runnable{public void run(){while(true){System.out.println("MyRunnable 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo3 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new MyRunnable());thread.start();while(true){System.out.println("主线程");Thread.sleep(1000);}}
}

 来运行

这次更能看到随机调度的现象

3)匿名内部类(Thread)

这几个其实用的都少,最多用到的还是lambda表达式

java">public class Demo4 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(){public void run(){while(true){System.out.println("Thread 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};thread.start();while(true){System.out.println("主线程");Thread.sleep(1000);}}
}

跟之前都一样,就是使用·匿名内部类了;

直接看运行

4)匿名内部类(Runnable)
java">public class Demo5 {public static void main(String[] args) throws InterruptedException {Thread thread =  new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("Thread 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});thread.start();while(true){System.out.println("主线程");Thread.sleep(1000);}}
}

一样嗷,匿名内部类创建Ruunable对象,

5)lambda表达式

这个才是我们使用最多的方法,主要是很方便;

lambda表达式:

(参数)->{实现了啥}

java">public class Demo6 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{while (true){System.out.println("Thread 线程");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();while(true){System.out.println("主线程");Thread.sleep(1000);}}
}

 方便吧,在new Thread的时候直接在括号中使用lambda表达式就行;

大家可能有疑问,不说重写run方法吗,这个{}里面的就是我们已经重写了,这个跟那个第三个匿名内部类的方法其实很像的;

我们来看运行结果;

完美嗷

2,Thread类及常见方法

Thread类是JVM用来管理线程的一个类,我们每创建一个Thread对象就有一个线程与他对应;

2.1 Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable

java"> Thread thread1 = new Thread();Thread thread2 = new Thread(new Runnable() {public void run() {}});Thread thread3 = new Thread(()->{while (true){System.out.println("线程3");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"线程3");Thread thread4 = new Thread(new Runnable() {public void run() {}},"线程4");

我们用4种构造方法创建了线程,我们来观察一下线程3,我们是否把线程的名字修改了呢;

 

 成功看到线程3了;

2.2 Thread的常见属性

属性获取方法
ID

getId()

名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrputed()

1) ID 类似进程的pid,线程的唯一标识,不同线程不会重复;

2) 名称 各种调试工具用到;

3) 线程当前所处的情况;

3) 通常来说优先级高的线程会容易调用;

4) 可以想象为饭局中的小程序员,对这次饭局不起决定性作用,JVM会在一个进程的所有非后台线程结束后结束;

我们可以使用SetDaemon()来把当前线程设置为后台线程;

5) run方法是否结束;

6) 终止线程运行;

我们来写一个代码获取所以线程信息;

java">public class Demo2 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "还活着");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(Thread.currentThread().getName() + "即将死亡");},"线程1");System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());thread.start();while (thread.isAlive()){System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());System.out.println(Thread.currentThread().getState() + " " + thread.getState());Thread.sleep(1000);}System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());}
}

 运行之后就能看到整个过程了;

2.3 启动一个线程

我们之前用过run方法来启动线程,实际上着并不是真正创建了线程,我们使用start真正在操作系统底层创建了一个线程,只有创建了线程对象再start才是让线程真正独立执行了;

2.4 中断一个线程

线程一旦工作就会等到任务结束才会停下来,但是有时候我们有让线程立即停下的需求,我们有两种办法来中断一个线程,其实叫终止更好,因为不是间断,而是线程就结束了;

我们来模拟一个场景,有两个员工张三,李四,老板让他们去给别人转账,张三正在转给骗子,李四及时阻止;

1,共享标记来中断线程

java">public class Demo3 {public static boolean a = true;public static void main(String[] args) {Thread thread1 = new Thread(()->{while (a){System.out.println(Thread.currentThread().getName() + "正忙着转账呢");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");},"张三");Thread thread2 = new Thread(()->{try {Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");a  = false;} catch (InterruptedException e) {throw new RuntimeException(e);}},"李四");thread1.start();thread2.start();}
}

来看运行结果 

哈哈哈哈哈,好玩吧; 

2,调用interrupt()方法来通知

方法说明
Thread对象.interrupt()中断对象关联的线程,如果线程正在阻塞,以异常方式通知,否则设置标志位
public static boolean interrputed();判读当前线程的标志位是否设置,调用后清除标志位;
public boolean 判读当前线程的标志位是否设置,调用后不清除标志位;

在Java线程的上下文中,中断标志位是Thread对象维护内部的布尔值用于表示该线程是否被请求中断。 

java">public class Demo3 {public static void main(String[] args) {Thread thread1 = new Thread(()->{//或者用Thread.interrupted();while (!Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName() + "正忙着转账呢");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");},"张三");Thread thread2 = new Thread(()->{try {Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");thread1.interrupt();} catch (InterruptedException e) {throw new RuntimeException(e);}},"李四");thread1.start();thread2.start();}
}

这里的原理就一样了,但是代码运行会报一个异常,

这个是因为

java">thread1.interrupt();

 唤醒了sleep让他直接抛出InterruptedException,被捕获到,抛出RuntimeException异常,所以我们在这里直接break就行;

这样结果就对了;

2.5 等待一个线程

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等待millis毫秒
public void join(long millis, int nanos)等待线程结束,精度更高;后面是纳秒;

 

java">public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(()->{while (true){System.out.println(Thread.currentThread().getName() + "线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"张三");Thread thread2 = new Thread(()->{while (true){System.out.println(Thread.currentThread().getName() + "线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}},"李四");thread1.start();thread1.join();thread2.start();thread2.join();System.out.println("全部线程打印结束");}
}

我们创建了两个线程和主线程,thread1.join意思为让主线程等待thread1线程执行完再执行 ,此时thread2还没开启,我们来看运行结果

 

 

张三始终在工作我天,因为我们使用的join没有放参数,是无休止的等待;如果我们放参数就不会这样傻傻的等待了, 

java">public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread thread1 = new Thread(()->{while (!Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName() + "线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}},"张三");Thread thread2 = new Thread(()->{while (!Thread.interrupted()){System.out.println(Thread.currentThread().getName() + "线程正在工作");try {Thread.sleep(1000);} catch (InterruptedException e) {break;}}},"李四");thread1.start();thread1.join(2000);thread1.interrupt();thread2.start();thread2.join(2000);thread2.interrupt();System.out.println("全部线程打印结束");}
}
");

修改一下代码。。。。。 

 

我们这回看到第一个join先让thread1插队,thread1运行2毫秒后,主线程启动,设置中断标志位,thread1停止,thread2插队,等待两毫秒后嗝屁,主线程也结束了; 

2.6 获取当前线程的引用

 这个之前我们就使用过了;

方法说明
public static Thread currentThread() 返回当前对象的引用,类似this

没啥好说的嗷,来段代码就好了;

java">public class Demo2 {public static void main(String[] args) {Thread thread = new Thread(()->{System.out.println(Thread.currentThread().getName());},"线程1");thread.start();System.out.println(Thread.currentThread().getName());}
}

 运行结果

2.7 休眠当前线程

这个也没啥好说的,我们一直在使用

方法说明
public static void sleep (long millis) throws InterputedException 休眠当前线程millis毫秒
public static void sleep (long millis,int nanos) throws InterputedException 更高精度

不演示了嗷,马上下一期


http://www.ppmy.cn/ops/156893.html

相关文章

【prompt实战】AI +OCR技术结合ChatGPT能力项目实践(BOL提单识别提取专家)

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 1. 需求背景 2. 目标 3. BOL通用处理逻辑…

DeepSeek vs. ChatGPT:不同的诞生时间,对人工智能发展的不同影响

DeepSeek vs. ChatGPT&#xff1a;不同的诞生时间&#xff0c;对人工智能发展的不同影响 ChatGPT 和 DeepSeek 诞生于不同的时间节点&#xff0c;代表了人工智能不同阶段的发展方向。它们在技术、应用以及对AI发展趋势的影响方面各有侧重。 1. 诞生时间与背景 ChatGPT&#x…

SQL中的REGEXP正则表达式使用指南

SQL中的REGEXP正则表达式使用指南 1. 基本语法 在SQL中使用REGEXP或RLIKE&#xff08;在MySQL中是同义词&#xff09;来进行正则表达式匹配&#xff1a; SELECT column_name FROM table_name WHERE column_name REGEXP pattern;2. 常用元字符 ^ - 匹配字符串开始位置$ - 匹…

Python的那些事第十四篇:Flask与Django框架的趣味探索之旅

Python Web应用开发&#xff1a;Flask与Django框架的趣味探索之旅 目录 第一章&#xff1a;Flask框架的奇幻之旅 第二章&#xff1a;Django框架的奇幻之旅 第三章&#xff1a;Flask与Django的魔法对决 第四章&#xff1a;总结 在Python的世界里&#xff0c;Web开发就像是一场…

【C++篇】C++11新特性总结1

目录 1&#xff0c;C11的发展历史 2&#xff0c;列表初始化 2.1C98传统的{} 2.2&#xff0c;C11中的{} 2.3&#xff0c;C11中的std::initializer_list 3&#xff0c;右值引用和移动语义 3.1&#xff0c;左值和右值 3.2&#xff0c;左值引用和右值引用 3.3&#xff0c;…

PyTorch torch.unbind、torch.split 和 torch.chunk函数介绍

pytorch中 torch.unbind、torch.split 和 torch.chunk等函数可用于张量的拆分操作。 1. torch.unbind 功能说明&#xff1a; torch.unbind 沿指定的维度将张量“解包”为多个张量&#xff0c;返回一个元组。解包后被操作的那个维度会消失&#xff0c;每个输出张量的维度数会比…

VUE之插槽

1、默认插槽 <template><div class"father"></div><h3>父组件</h3><div class"content"><Category title"热门游戏列表"><ul><li v-for"g in games" :key"g.id">{{…

基于ArcGIS的SWAT模型+CENTURY模型模拟流域生态系统水-碳-氮耦合过程研究

流域是一个相对独立的自然地理单元&#xff0c;它是以水系为纽带&#xff0c;将系统内各自然地理要素连结成一个不可分割的整体。碳和氮是陆地生态系统中最重要的两种化学元素&#xff0c;而在流域系统内&#xff0c;水-碳-氮是相互联动、不可分割的耦合体。随着流域内人类活动…