学习JavaEE的日子 Day29 yield,join,线程的中断,守护线程,线程局部变量共享,线程生命周期

news2024/9/25 9:40:55

Day29

多线程

12. 线程的礼让

Thread.yield();

理解:此方法为静态方法,此方法写在哪个线程中,哪个线程就礼让

注意:所谓的礼让是指当前线程退出CPU资源,并转到就绪状态,接着再抢

需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次,观察实验结果

public class Test01 {
	public static void main(String[] args) {
		
		A a = new A();
		B b = new B();
		
		a.start();
		b.start();
	}
}
public class A extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("A:" + i);
		}
	}
}
public class B extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("B:" + i);
			
			//礼让:让当前线程退出CPU资源,当前线程退出后立刻转入抢资源的状态,可能又会抢到CPU资源
			Thread.yield();
		}
	}
}

13. 线程的合并

t.join(); 合并方法

需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		for (int i = 1; i <=200; i++) {
			System.out.println("主线程:" + i);
			if(i == 10){
				//让t线程加入到当前线程
				t.join();
			}
		}
		
	}
}
public class MyThread extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <=200; i++) {
			System.out.println("子线程:" + i);
		}
	}
}

14.线程的中断

14.1 线程的中断1

面试题:下列代码的子线程开启后,是否会在3000毫秒就被销毁?
答:不一定,因为3000毫秒后主线程才休眠结束,这时会抢CPU资源
如果立刻抢到,那么子线程就是3000毫秒后销毁
如果没有抢到CPU资源,那么子线程会继续运行,直到主线程抢到CPU资源

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		
		MyThread t = new MyThread();//子线程
		t.start();
        
		//主线程休眠3秒(Main方法就是主线程)
		Thread.sleep(3000);
		//过时了
		t.stop();//立刻停止(缺点:可能会导致功能缺失)	
		
	}
	
}
public class MyThread extends Thread{

	@Override
	public void run() {
		while(true){ //死循环
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}
}
14.2 线程的中断2

另外一种写法:

该方法一定会执行到444结束,不像上面一种立马停止,可能在222或者333结束

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		Thread.sleep(3000);
		
		t.setFlag(false);
		
	}
	
}
public class MyThread extends Thread{

	private boolean flag = true;
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}

	@Override
	public void run() {
		while(flag){
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}
}

14.3 线程的中断3

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		Thread.sleep(3000);
		
		//改变线程状态
		t.interrupt();
	}
	
}
public class MyThread extends Thread{


	@Override
	public void run() {
		
		//获取线程状态(是否消亡)
//		System.out.println(Thread.currentThread().isInterrupted());
		
		while(!Thread.currentThread().isInterrupted()){
			
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}
}

15.守护线程/后台线程

守护线程 默默守护着前台线程,当所有的前台线程都消亡后,守护线程会自动消亡

注意:垃圾回收器就是守护线程

t.setDaemon(true);

public class Test01 {
	public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.setDaemon(true);//将当前线程设置为守护线程
		t.start();
		
		for (int i = 1; i <= 5; i++) {
			System.out.println("主线程:" + i);
			Thread.sleep(1000);
		}
		
	}
}
public class MyThread extends Thread{

	@Override
	public void run() {
		while(true){
			System.out.println("后台线程默默守护着前台线程");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

十六、线程局部变量(实现线程范围内的共享变量)-- ThreadLocal

16.1 共享单个数据
public class Test01 {
	public static final ConcurrentHashMap<Thread, Integer> map = new ConcurrentHashMap<>();
	
	public static void main(String[] args) {
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 10;
				
				//存数据
				map.put(Thread.currentThread(), i);
				
				A a = new A();
				B b = new B();
				a.println();//10
				b.println();//10
			}
		},"线程1").start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 20;
				
				//存数据
				map.put(Thread.currentThread(), i);
				
				A a = new A();
				B b = new B();
				a.println();//20
				b.println();//20
				
			}
		}, "线程2").start();
		
	}
}
public class A {

	public void println(){
		Thread t = Thread.currentThread();
		Integer value = Test01.map.get(t);
		System.out.println(t.getName() + "里的A类对象获取了数据:" + value);
	}
}
public class B {

	public void println(){
		Thread t = Thread.currentThread();
		Integer value = Test01.map.get(t);
		System.out.println(t.getName() + "里的B类对象获取了数据:" + value);
	}
}
16.2 共享多个数据-- ThreadLocal

存数据
local.set(data)底层原理:
1.获取当前线程对象
2.通过当前线程对象获取ThreadLocalMap<ThreadLocal,T>
3.map.put(this,t) * 获取数据

local.get()底层原理:
1.获取当前线程对象
2.通过当前线程对象获取ThreadLocalMap<ThreadLocal,T>
3.map.getEntry(this) -> Entry对象
4.entry.getValue()

public class Test01 {
	public static final ThreadLocal<Data> local = new ThreadLocal<>();
	
	public static void main(String[] args) {
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				Data data = Data.getInstance(10,"xxx");
				
				/**
				 * 存数据
				 * local.set(data)底层原理:
				 * 		1.获取当前线程对象
				 * 		2.通过当前线程对象获取ThreadLocalMap<ThreadLocal,T>
				 * 		3.map.put(this,t)
				 */
				local.set(data);
				
				A a = new A();
				B b = new B();
				a.println();//10
				b.println();//10
			}
		},"线程1").start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				
				Data data = Data.getInstance(20,"yyy");
				data = Data.getInstance(30,"zzz");
				
				//存数据
				local.set(data);
				
				A a = new A();
				B b = new B();
				a.println();//20
				b.println();//20
				
			}
		}, "线程2").start();
		
	}
}

数据包类

//数据包类
public class Data {
	
	private int i;
	private String str;
	
    //有参构造,无参构造,get,set方法省略
	
	//保证每个线程里只有一个Data包对象
	public static Data getInstance(int i,String str){
		Data data = Test01.local.get();//获取当前线程的Data对象
		if(data == null){
			data = new Data(i, str);
			Test01.local.set(data);
		}else{
			data.setI(i);
			data.setStr(str);
		}
		return data;
	}

	@Override
	public String toString() {
		return "Data [i=" + i + ", str=" + str + "]";
	}
}
public class A {

	public void println(){
		Thread t = Thread.currentThread();
		/**
		 * 获取数据
		 * local.get()底层原理:
		 * 		1.获取当前线程对象
		 * 		2.通过当前线程对象获取ThreadLocalMap<ThreadLocal,T>
		 * 		3.map.getEntry(this) -> Entry对象
		 * 		4.entry.getValue()
		 */
		Data value = Test01.local.get();
		System.out.println(t.getName() + "里的A类对象获取了数据:" + value);
	}
}
public class B {

	public void println(){
		Thread t = Thread.currentThread();
		Data value = Test01.local.get();
		System.out.println(t.getName() + "里的B类对象获取了数据:" + value);
	}
}

17.线程的生命周期

1、新建状态

i. 在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时,它已经有了相应的内存空间和其它资源,但还处于不可运行状态。新建一个线程对象可采用线程构造方法来实现。

ii. 例如:Thread thread=new Thread();

2、 就绪状态

i. 新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU调用,这表明它已经具备了运行条件。

3、运行状态

i. 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。

4、 阻塞状态

i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

5、死亡状态

i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。

在这里插入图片描述

总结

1.线程的礼让 – yield

2.线程的合并 – join

3.线程的中断

4.守护线程

5.线程局部变量共享 – 重要

用,这表明它已经具备了运行条件。

3、运行状态

i. 当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。

4、 阻塞状态

i. 一个正在执行的线程在某些特殊情况下,如被人为挂起,将让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep(2000)、wait()等方法,线程都将进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以转入就绪状态。

5、死亡状态

i. 线程调用stop()方法时或run()方法执行结束后,线程即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。

在这里插入图片描述

简答题

1.Thread类中的yield方法有什么作用?

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。它是一个静态方法而且

只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入

到暂停状态后马上又被执行。

2.sleep()和wait() 有什么区别?

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。

当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备,获取对象锁进入运行状态。

3.有三个线程T1,T2,T3,如何保证顺序执行?

在多线程中有多种方法让线程按特定顺序执行,你可以用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用 T2,T2调用T1),这样T1就会先完成而T3最后完成。 实际上先启动三个线程中哪一个都行,因为在每个线程的run方法中用join方法限定了三个线程的执行顺序。

public class JoinTest2 {
	// 1.现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
	public static void main(String[] args) {
		final Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("t1");
			}
		});
		final Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					// 引用t1线程,等待t1线程执行完
					t1.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("t2");
			}
		});
		Thread t3 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					// 引用t2线程,等待t2线程执行完
					t2.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("t3");
			}
		});
		t3.start();//这里三个线程的启动顺序可以任意,大家可以试下!
		t2.start();
		t1.start();
	}
}

简答题

如何停止一个正在运行的线程

1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2、使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend一样都是过期作废的方法。

3、使用interrupt方法中断线程。

class MyThread extends Thread {
volatile boolean stop = false;
public void run() {
while (!stop) {
System.out.println(getName() + " is running");
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println("week up from blcok...");
stop = true; // 在异常处理代码中修改共享变量的状态
}
}
System.out.println(getName() + " is exiting...");
}
}
class InterruptThreadDemo3 {
public static void main(String[] args) throws InterruptedException {
MyThread m1 = new MyThread();
System.out.println("Starting thread...");
m1.start();
Thread.sleep(3000);
System.out.println("Interrupt thread...: " + m1.getName());
m1.stop = true; // 设置共享变量为true
m1.interrupt(); // 阻塞时退出阻塞状态
Thread.sleep(3000); // 主线程休眠3秒以便观察线程m1的中断情况
System.out.println("Stopping application...");
}
}

总结

1.线程的礼让 – yield

2.线程的合并 – join

3.线程的中断

4.守护线程

5.线程局部变量共享 – 重要

6.线程生命周期 — 重要

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

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

相关文章

P8764 [蓝桥杯 2021 国 BC] 二进制问题

很板的一道题目&#xff0c;注意就是数组别开的太小 #include<bits/stdc.h> using namespace std; using ll long long; using pii pair<int,int>; #define int long long const int N 1e510; const int inf 0x3f3f3f3f; const int mod 1e97; int gcd(int a,…

【81-100】计算机网络基础知识(非常详细)从零基础入门到精通,看完这一篇就够了

【81-100】计算机网络基础知识&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了 以下是本文参考的资料 欢迎大家查收原版 本版本仅作个人笔记使用81、对于FIN_WAIT_2&#xff0c;CLOSE_WAIT状态和TIME_WAIT状态&#xff1f;你知道多少?82、你…

算法第三十四天-有效数独

有效数独 题目要求 解题思路 一个简单的方法是&#xff0c;遍历9*9书读三次&#xff0c;以确保&#xff1a; 行中没有重复的数字列中没有重复的数字3*3子数独中没有重复的数字 但是&#xff0c;实际上&#xff0c;所有的一切都以可以在一次迭代中完成 可以使用box_index (r…

Kubernetes Gateway API 介绍

Kubernetes Gateway API 诞生背景 在 kubernetes 中&#xff0c;流量的治理主要分为两个部分&#xff1a; 南北向流量东西向流量 南北向流量&#xff08;NORTH-SOUTH traffic&#xff09; 在计算机网络中&#xff0c;南北向流量通常指数据流量从一个**内部网络&#xff08;…

20231911马星 2022-2023-2 《网络攻防实践》实验四

1.实践内容 在网络攻防实验环境中完成TCP/IP协议栈重点协议的攻击实验&#xff0c;包括ARP缓存欺骗攻击、ICMP重定向攻击、SYN Flood攻击、TCP RST攻击、TCP会话劫持攻击。 1.1 ARP欺骗攻击&#xff1a; &#xff08;1&#xff09;ARP欺骗&#xff08;ARP spoofing&#xff09…

【算法-PID】

算法-PID ■ PID■ 闭环原理■ PID 控制流程■ PID 比例环节&#xff08; Proportion&#xff09;■ PID 积分环节&#xff08;Integral&#xff09;■ PID 微分环节&#xff08;Differential&#xff09; ■ PID PID 分别是 Proportion&#xff08;比例&#xff09;、 Integr…

如何购买小程序模板开发企业小程序

在当今数字化时代&#xff0c;小程序已成为企业展示与营销的重要工具。购买一个小程序模板&#xff0c;来快速上线自己的企业小程序&#xff0c;已成为当前最流行且性价比最高的开发方式。 乔拓云网站提供了丰富的小程序模板资源&#xff0c;无论你需要哪种风格或功能的小程序&…

WOT全球技术创新大会2024北京站:技术派与市场派共话AIGC新未来

WOT全球技术创新大会2024将在北京盛大开幕&#xff0c;聚焦AIGC时代的软件工程新范式、AI Agent探索与应用、多模态AIGC前沿探索以及大模型部署与应用等核心议题。此次大会将促进技术信仰派与市场信仰派间的深度对话与交流&#xff0c;共同探讨AIGC技术的未来发展方向。 在过去…

【Java程序设计】【C00351】基于Springboot的疫情居家办公系统(有论文)

基于Springboot的疫情居家办公系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 &#x1f345;文末点击卡片获取源码&#x1f345; 开发环境 运行环境&#xff1a;推荐jdk1.8&#xff1b; 开发工具&#xff1a;eclipse以及i…

Flask python 开发篇:上传文件(在指定目录下创建文件夹)

flask上传文件以及常见问题 一、flask文档上传文件的介绍二、上传文件的实现2.1、生成一个from表单&#xff0c;用来提交图片2.2、编写上传的逻辑 三、运行代码、以及常见异常四、写在最后 一、flask文档上传文件的介绍 Flask上传文件的文档介绍&#xff0c;文件上传的基本思想…

快递鸟物流轨迹地图API接口,包裹行程尽在掌握!

在快节奏的现代生活中&#xff0c;物流行业作为连接生产者与消费者的桥梁&#xff0c;其重要性不言而喻。随着电子商务的飞速发展&#xff0c;人们对物流信息的实时性和准确性要求越来越高。为了满足这一需求&#xff0c;快递鸟物流轨迹地图API应运而生&#xff0c;为广大用户提…

WIFI驱动移植实验: wireless tools 工具测试

一. 简介 前面一篇文章交叉编译了 wireless tools 工具&#xff0c;并移植到开发板文件系统上。文章如下&#xff1a; WIFI驱动移植实验&#xff1a; wireless tools 工具移植-CSDN博客 本文对 所移植的操作 WIFI设备的工具 wireless tools进行测试。确认是否可以使用。 二…

【浅尝C++】使用模板实现泛型编程第一弹=>函数模板/类模板/模板匹配原则/函数模板原理

&#x1f3e0;专栏介绍&#xff1a;浅尝C专栏是用于记录C语法基础、STL及内存剖析等。 &#x1f6a9;一些备注&#xff1a;之前的文章有点杂乱&#xff0c;这里将前面的知识点重新组织了&#xff0c;避免了过多冗余的废话。 &#x1f3af;每日努力一点点&#xff0c;技术变化看…

如何删除Excel中的空白行?这里提供详细步骤

要从数据集中删除所有空白行吗&#xff1f;如果是这样&#xff0c;Microsoft Excel提供自动和手动方法来清除空白行并向上移动数据。下面是如何使用这些方法。 删除空白行时&#xff0c;Excel会删除整行并上移数据&#xff0c;以便数据集中不再有空行。记住&#xff0c;你也可…

linux下使用迅雷的完美办法(网络版免费),其他下载工具

迅雷有自家服务器的支持&#xff0c;因此&#xff0c;其他下载器&#xff0c;可能难以匹敌 &#xff1f; linux下使用迅雷的完美办法&#xff08;免费&#xff09; https://blog.csdn.net/lqrensn/article/details/8853949 网络版 Linux下安装并使用迅雷 https://www.lxlin…

【微服务】Nacos(注册中心)

文章目录 1.基本介绍1.概述2.Nacos下载和运行&#xff08;java8/maven3.2.x&#xff09;1.解压到没有中文路径的2.双击startup3.浏览器输入http://192.168.242.124:8848/nacos4.用户名和密码为nacos5.cmd输入netstat -anb | more查看监听端口 2.创建Nacos服务提供者 100041.项目…

人工智能在产业中应用--生成智能

二、生成式人工智能 前面介绍了很多人工智能的应用&#xff0c;接下来部分我们会介绍当前正在进行的生成智能。生成智能和以往的人工智能有什么区别&#xff0c;个人觉得主要区别就在于“度”。在表现上就是以前的人工智能更多是利用既有的数据集分布挖掘和解决在这个数据集下…

WPF 命名空间解释

在C#中有命名空间的概念&#xff0c;我们可以使用using引入&#xff0c;就可以使用其中的类&#xff0c;在xaml中&#xff0c;也同样有命名空间&#xff0c;在window标签中用xmlns声明的这几行&#xff0c;这就是本页面引入的命名空间。 一般的情况下&#xff0c;我们引入命名空…

计算机网络:传输控制协议(Transmission Control Protocol-TCP协议

计算机网络&#xff1a;传输控制协议&#xff08;Transmission Control Protocol-TCP协议&#xff09; 本文目的前置知识点TCP协议简介主要特性通信流程1. 建立连接的过程(三次握手&#xff0c;243)1.1 为什么要三次握手&#xff0c;两次不行吗&#xff1f; 2. 释放连接的过程(…

在Linux上使用nginx反向代理部署Docker网站

在政务云上部署Web环境&#xff0c;为了保证服务器安全&#xff0c;甲方只开放一个端口且只允许使用https协议进行访问&#xff0c;经过思考&#xff0c;决定使用docker部署网站&#xff0c;使用nginx反向代理&#xff0c;通过不同的二级域名访问不同的端口。 1 使用docker部署…