Java第二十章多线程

news2024/11/17 7:30:00

线程简介

在 Java 中,并发机制非常重要。在以往的程序设计中,我们都是一个任务完成后再进行下一个任务,这样下一个任务的开始必须等待前一个任务的结束。Java 语言提供了并发机制,程序员可以在程序中执行多个线程,每一个线程完成一个功能,并与其他线程并发执行,这种机制被称为多线程。然而,有必要强调的是,并不是所有编程语言都支持多线程。
 

创建线程

在 Java 中,主要提供两种方式实现线程,分别为继承 java.lang.Thread 类与实现 java.lang.Runnable接口。

继承Thread类
Thread 类是 java.lang 包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立 Thread 实例。Thread 类中常用的两个构造方法如下:

public Thread():创建一个新的线程对象。
 public Thread(String threadName): 创建一个名称为 threadName 的线程对象。
继承 Thread 类创建一个新的线程的语法如下:

public class ThreadTest extends Thread{

}

Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在

run()方法中。run()方法必须使用以下语法格式:

public void run(){
}

主方法线程自动由 Java虚拟机负责,程序员负责启动自己的线程。代码如下:

public static void main(Stringl args) {
new ThreadTest().start();

}

代码:

package 二十;
 
public class ThreadTest extends Thread{
	public void run() {
		for(int i = 1;i<=10;i++) {
			System.out.print(i+" ");
		 }
	}
	public static void main(String[] args) {
		ThreadTest t =new ThreadTest();
		t.start();
	}
 
}

运行结果如下:

 

实现Runnable接口

到目前为止,线程都是通过扩展 Thread 类来创建的,如果程序员需要继承其他类(非 Thread 类)而且还要使当前类实现多线程,那么可以通过 Runnable 接口来实现。例如,一个扩展Frame 类的 GUI程序不可能再承 Thread 类,因为 Java 语言中不支持多继承,这时该类需要实现 Runnable 接口使其具有使用线程的功能。实现 Runnable 接口的语法如下:

public class Thread extends Object implements Runnable

Threa类中有以下两个构造方法:

  •  public Thread(Runnable target)。
  • public Thread(Runnable target,String name)。

这两个构造方法的参数中都存在 Runnable 实例,使用以上构造方法就可以将 Runnable 实例与
Thread 实例相关联。使用 Runnable 接口启动新的线程的步骤如下:

  1.  建立 Runnable 对象。
  2. 使用参数为 Runnable 对象的构造方法创建 Thread 实例。
  3.  调用 start()方法启动线程。

通过 Runnable 接口创建线程时,程序员首先需要编写一个实现 Runnable 接口的类,然后实例化该类的对象这样就建立了 Runnable 对象;接下来使用相应的构造方法创建 Thread 实例;最后使用该实例调用 Thread 类中的start()方法启动线程。 表明了实现 Runnable 接口创建线程的流程。
代码:

package 二十;
 
import java.awt.Container;
 
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
 
public class SwingAndThread extends JFrame{
	int count = 0;	//图标横坐标
	
	public SwingAndThread() {		
		setBounds(400,400,500,500);	//绝对定位窗体大小与位置
		Container container = getContentPane();		//主容器
		container.setLayout(null);		//使窗体不使用任何布局管理器
		
		
		Icon icon = new ImageIcon("/D:/Java/eclipse/二十/src/西瓜.png");	//图标对象
		JLabel jL = new JLabel(icon);	//显示图标的标签
		jL.setBounds(100, 100, 200, 500);	//设置标签的位置与大小
		Thread t = new Thread() {	//定义匿名线程对象
			public void run() {
				while (true) {
					jL.setBounds(count, 100, 500, 200);	//将标签的横坐标用变量表示
					try {
						Thread.sleep(500);	//使线程休眠500毫秒
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					count += 9;	//使横坐标每次增加9
					if(count >=200){
						count = 10;	//当坐标到达标签的最右边时,使其回到标签最左边
					}
				}
			}
		};
		t.start();	//启动线程
		container.add(jL);	//将标签添加到容器中
		setVisible(true);	//使窗体可见
		setDefaultCloseOperation(EXIT_ON_CLOSE);	//设置窗体的关闭方式
	}
	public static void main(String[] args) {
		new SwingAndThread();
	}
 
}

 运行结果如下:

线程的生命周期

 一旦线程进入可执行状态,它会在就绪与运行状态下转换,同时也有可能进入等待,休眠,赌塞或死亡状态。

要使线程处于就绪,有以下几种方法:

  • 调用 sleep() 方法。
  • 调用 wait() 方法。
  • 等待输入/输出完成。

当线程处于就绪状态后,可以用以下几种方法使线程再次进入运行状态:

  • 线程调用 notify() 方法。
  • 线程调用 notifyAll() 方法。
  • 线程调用 interrupt() 方法。
  • 线程的休眠时间结束。
  • 输入/输出结束。

操作线程的方法

线程的休眠

一种能控制线程行为的方法是调用 sleep() 方法需要一个参数用于指定该线程休眠的时间,该时间以毫秒为单位。

例:每0.1秒绘制一条随机颜色的线条

package 二十;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
 
import javax.swing.JFrame;
 
public class SleepMethodTest extends JFrame{
	private static Color[] color= {Color.BLACK,Color.BLUE,Color.CYAN,Color.GREEN,
		Color.RED,Color.ORANGE,Color.YELLOW,Color.PINK,Color.LIGHT_GRAY};//定义颜色数组
	private static final Random rand=new Random();//创建随机对象
	
	private static Color getC() {//获取随机颜色值的方法
		return color[rand.nextInt(color.length)];
	}
	
	public  SleepMethodTest(){
		Thread t=new Thread(new Runnable() {//创建匿名线程对象
			int x=30;//定义初始坐标
			int y=50;
			
			public void run() {
				while(true) {//无限循环
					try {
						Thread.sleep(100);//线程休眠0.1秒
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					Graphics graphics=getGraphics();//获取组件绘图上下文对象
					graphics.setColor(getC());//设置绘图颜色
					graphics.drawLine(x, y,150, y++);//绘制直线并递增垂直坐标
					if(y>=180) {
						y=50;
					}
				}
			}
		});
		t.start();//启动线程
	}
 
	public static void main(String[] args) {
		init(new SleepMethodTest(),200,200);
 
	}
 
	public static void init(JFrame frame,int width,int height) {//初始化程序界面的方法
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(width, height);
		frame.setVisible(true);
	}
}

运行结果如下:

线程的加入

当某个线程使用 join() 方法的加入一个线程时,另外一个线程会等待该线程执行完毕后再继续执行。

代码:

package 二十;
 
import java.awt.BorderLayout;
 
import javax.swing.JFrame;
import javax.swing.JProgressBar;
 
public class JoinTest extends JFrame{
	private Thread threadA;		//定义两个线程
	private Thread threadB;		
	private JProgressBar progressBar = new JProgressBar();	//定义两个进度条组件
	private JProgressBar progressBar2 = new JProgressBar();
	
	
	public static void main(String[] args) {
		JoinTest test= new JoinTest();
		test.setVisible(true);
 
	}
	
	public JoinTest() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(200,200,200,100);
		getContentPane().add(progressBar,BorderLayout.NORTH);	//将进度条设置在窗体最北面
		getContentPane().add(progressBar2,BorderLayout.SOUTH);	//将进度条设置在窗体最南面
		progressBar.setStringPainted(true);		//设置进度条显示数字字符
		progressBar2.setStringPainted(true);
		threadA = new Thread(new Runnable() {	//使用匿名内部类形式初始化Thread实例
			int count = 0;
			public void run() {		//重写run()方法
				while(true) {
					progressBar.setValue(++count);	//设置进度条的当前值
					try {
						Thread.sleep(100);	//使线程A休眠100毫秒
						threadB.join();	//使线程B调用join()方法
					}catch(InterruptedException e) {
						e.printStackTrace();
				}
			}
		}
	});
		threadA.start();	//启动线程A
		threadB = new Thread(new Runnable() {
			int count = 0;
			public void run() {
				while(true) {
					progressBar2.setValue(++count);	//设置进度条的当前值
					try {
						Thread.sleep(100);	//使线程B休眠100毫秒
			
					}catch(InterruptedException e) {
						e.printStackTrace();
				}
					if(count == 100);	//当count变量增长为100时
					break;	//跳出循环
			}
		}
	});
		threadB.start();	//启动线程B
 
	}
}

运行结果如下:

线程的中断

以往有时候会使用 stop() 方法停止线程,但当前版本的 JDK 早已废除了 stop() 方法,不建议使用 stop() 方法来停止一个线程的运行。现在提倡在 run() 方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

 如果线程是因为使用了 sleep()或 wait()方法进入了就入就绪状态,可以使用 Thread()方法,同时程序破除了 InterruptedException 异常,在异常处理时结束了 while 循环。在项目中,经常在这里执行关闭数据连接和关闭 Socket 连接等操作。
代码:

package 二十;
 
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
 
public class InterruptedSwing extends JFrame{
	
	public InterruptedSwing() {
		JProgressBar progressBar = new JProgressBar();	//创建进度条
		getContentPane().add(progressBar,BorderLayout.NORTH);	//将进度条设置在窗体合适位置
		JButton button = new JButton("停止");
		getContentPane().add(button,BorderLayout.SOUTH);
		progressBar.setStringPainted(true);	//设置进度条上显示数字
		Thread t= new Thread(new Runnable() {	
			int count = 0;
			public void run() {		
				while(true) {
					progressBar.setValue(++count);	//设置进度条的当前值
					try {
						Thread.sleep(100);	//使线程休眠100毫秒
					}catch(InterruptedException e) {	//捕捉InterruptedException异常
						System.out.println("当前线程序被中断");
						break;
				}
			}
		}
	});
		
		button.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				t.interrupt();	//中断线程
				
			}
		});
		t.start();	//启动线程
	}
	
	public static void init(JFrame frame,int width,int height) {
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(width, height);
		frame.setVisible(true);
	}
 
	public static void main(String[] args) {
		init(new InterruptedSwing(),100,100);
	}
 
}

运行结果如下:

线程的礼让

Thread 类提供了一种礼让方法,使用 yied()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。

        yied()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时再度回到就绪状态。对于支持多任务的操作系统来说,不需要调用 yied()方法,因为操作系统会为线程自动分配 CPU 时间来执行。

线程的优先级

每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而只是它运行的概率比较小,如垃圾回收线程的优先级就按照较低。

        线程的优先级可以使用 setPriority()方法调整,如果使用该方法设置的优先级不在 1~10,将产生IllegalArgumentException 异常。

代码:

package 二十;
 
public class PriorityTest implements Runnable{
	String name;
	
	public PriorityTest(String name) {
		this.name = name;
	}
	
	@Override
	public void run() {
		String tmp = "";
		for(int i = 0;i < 50000; i++) {	//完成5万次字符串拼接
			tmp += i;
		}
		System.out.println(name + "线程完成任务");
		
	}
 
 
	public static void main(String[] args) {
		Thread a = new Thread(new PriorityTest("A"));
		a.setPriority(1);		//A线程优先级最小
		Thread b = new Thread(new PriorityTest("B"));
		b.setPriority(3);
		Thread c = new Thread(new PriorityTest("C"));
		c.setPriority(7);
		Thread d = new Thread(new PriorityTest("D"));
		d.setPriority(10);	//D线程优先级最大
		a.start();
		b.start();
		c.start();
		d.start();
	}
}

运行结果如下:

线程同步

在单线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,如两个人同事说话、两个人同时过同一个独木桥。所以,在多线程编程中需要防止这些资源访问的冲突。Java 提供了线程同步的机制来防止资源访问的冲突。

线程安全

  在编写多线程时时,因该考虑到线程安全问题。实质上线程问题来源两个线程同时存取单一对象的数据。

代码:

package 二十;
 
public class ThreadSafeTest implements Runnable{
	int num = 10;	//设置当前票数
	
	
	@Override
	public void run() {
		while(true) {	//设置无限循环
			if(num >0) {	//设置当前票数是否大于0
				try {
					Thread.sleep(100);	//使当前线程休眠100毫秒
				}catch(InterruptedException e) {
						e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"------票数"+num--);	//票数减一
			}
		}
	}
 
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest();	//实例化类对象
		Thread tA = new Thread(t,"线程一");	//以该类对象分别实例化4个线程
		Thread tB = new Thread(t,"线程二");
		Thread tC = new Thread(t,"线程三");
		Thread tD = new Thread(t,"线程四");
		tA.start();	//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	}
}

运行结果如下:

线程同步机制

 所以解决多线程资源问题的方法基本上都是采用给定时间只允许一个线程访问共享资源的方法。这时就需要给共享源上一道锁。

1、同步块

 Java中提供了同步机制,可以有效地防止资源冲突。同步机制使用 synchronized 关键字,使用该关键字包含的代码块称为同步块,也称临界区,语法如下:

synchronized(Object){

}

 代码:

package 二十;
 
public class ThreadSafeTest implements Runnable{
	int num = 10;	//设置当前票数
	
	
	@Override
	public void run() {
		while(true) {	//设置无限循环
			synchronized(this) {	//同步锁
			if(num >0) {	//设置当前票数是否大于0
				try {
					Thread.sleep(100);	//使当前线程休眠100毫秒
				}catch(InterruptedException e) {
						e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"------票数"+num--);	//票数减一
			}
		}
	}
}
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest();	//实例化类对象
		Thread tA = new Thread(t,"线程一");	//以该类对象分别实例化4个线程
		Thread tB = new Thread(t,"线程二");
		Thread tC = new Thread(t,"线程三");
		Thread tD = new Thread(t,"线程四");
		tA.start();	//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	}
}

运行结果如下:

2、同步方法

同步方法就是在方法前面用 synchronized 关键字修饰的方法,语法如下:

synchronized void f(){

}

 代码:

package 二十;
 
public class ThreadSafeTest implements Runnable{
	int num = 10;	//设置当前票数
	public synchronized void doit() {	//定义同步方法
		if(num >0) {	//设置当前票数是否大于0
			try {
				Thread.sleep(10);	//使当前线程休眠100毫秒
			}catch(InterruptedException e) {
					e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"------票数"+num--);	//票数减一
		}
	}
	
	@Override
	public void run() {
		while(true) {	//设置无限循环
			doit();	//在run()方法中调用该同步方法
		}
}
	
	public static void main(String[] args) {
		ThreadSafeTest t = new ThreadSafeTest();	//实例化类对象
		Thread tA = new Thread(t,"线程一");	//以该类对象分别实例化4个线程
		Thread tB = new Thread(t,"线程二");
		Thread tC = new Thread(t,"线程三");
		Thread tD = new Thread(t,"线程四");
		tA.start();	//分别启动线程
		tB.start();
		tC.start();
		tD.start();
	}
}

运行结果如下:

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

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

相关文章

Mysql的二阶段提交

先看执行器与InnoDB引擎是如何更新一条指定的数据的 可以看到&#xff0c;InnoDB在写redo log时&#xff0c;并不是一次性写完的&#xff0c;而有两个阶段&#xff0c;Prepare与Commit阶段&#xff0c;这就是"两阶段提交"的含义。 为什么要写redo log&#xff0c;不…

在虚拟机搭建nignx,和使用本地访问nginx的情况

下载nginx yum install nginx 查看nginx是否安装成功。 nginx -v nginx的配置文件的目录和资源的目录。 先到nginx.conf的目录下&#xff0c;在 /etc/nginx/nginx.conf&#xff0c;编辑它。 vi /etc/nginx/nginx.conf 可以看到默认的html的目录。在 /usr/share/nginx/html 下面…

滑块验证码之图片距离计算

滑块验证码之图片距离计算 1.使用工具 vscodepython3.8 2.安装opencv-python python -m pip install opencv-python -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com查看安装的版本&#xff1a; C:\Users\wenhz>pip list|findstr opencv opencv-pyt…

SRM供应商询价招投标管理系统

前言&#xff1a; 在当今高度信息化的时代&#xff0c;企业运营的效率和透明度对于企业的生存和发展至关重要。供应商管理系统&#xff08;SRM&#xff09;作为企业采购管理的重要工具&#xff0c;旨在提升企业与供应商之间的协作效率&#xff0c;优化采购流程&#xff0c;降低…

MySOL常见四种连接查询

1、内联接 &#xff08;典型的联接运算&#xff0c;使用像 或 <> 之类的比较运算符&#xff09;。包括相等联接和自然联接。 内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。例如&#xff0c;检索 students和courses表中学生标识号相同的所有行。 2、…

U-Net及其变体在医学图像分割中的应用研究综述

U-Net及其变体在医学图像分割中的应用研究综述 论文来自&#xff1a;中国生物医学工程学报 2022 摘 要&#xff1a; 医学图像分割可以为临床诊疗和病理学研究提供可靠的依据&#xff0c;并能辅助医生对病人的病情做出准确的判断。 基于深度学习的分割网络的出现解决了传统自动分…

每日一练2023.11.28———【PTA】

题目链接&#xff1a; L1-009 N个数求和 题目要求&#xff1a; 本题的要求很简单&#xff0c;就是求N个数字的和。麻烦的是&#xff0c;这些数字是以有理数分子/分母的形式给出的&#xff0c;你输出的和也必须是有理数的形式。 输入格式&#xff1a; 输入第一行给出一个正整…

SpringBoot+Redis编写一个抢红包雨的案例。附源码。

案例演示 SpringBootRedis编写一个抢红包雨的案例。附源码 1、案例分析&#xff0c;整体方案介绍 预备上线一个红包雨活动。这个红包雨的思路是活动开始前25分钟&#xff0c;在后台创建活动。然后前端用户进入&#xff0c;到点后将设置的金额拆分成多个小红包&#xff0c;开启倒…

C++之STL库:string类(用法列举和总结)

前言 大家在学习STL库的时候一定要学会看英文文档&#xff0c;俗话说熟能生巧&#xff0c;所以还得多练&#xff01; 在使用string类之前&#xff0c;要包含头文件#include <string>和using namespace std; 文档链接&#xff1a;string - C Reference 一、string——构造…

【API 自动化测试】Eolink Apikit 图形用例详解

Eolink Apikit 的图形用例是指通过图形化的方式去表现 API 流程测试。它包括了条件选择器、单个 API 步骤和操作集等组件。 相较于前面推荐的表格化的通用用例&#xff0c;图形用例可以让测试人员更方便地设计和管理 API 流程测试&#xff0c;同时也更加的灵活。 添加图形用例…

第29期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

【密码学引论】序列密码

第五章 序列密码 1、序列密码 定义&#xff1a; 加密过程&#xff1a;把明文与密钥序列进行异或运算得到密文解密过程&#xff1a;把密文与密钥序列进行异或运算得到明文以字/字节为单位加解密密钥&#xff1a;采用一个比特流发生器随机产生二进制比特流 2、序列密码和分组密…

前馈式神经网络与反馈式神经网络的区别,联系,各自的应用范围和场景!!!

文章目录 前言一、前馈式神经网络是什么&#xff1f;二、前馈式神经网络包括&#xff1a;三、反馈式神经网络是什么&#xff1f;四、反馈式神经网络包括&#xff1a;总结 前言 前馈式神经网络和反馈式神经网络是两种主要的神经网络架构&#xff0c;它们在网络结构和应用场景上…

OpenCV快速入门【完结】:总目录——初窥计算机视觉

文章目录 前言目录1. OpenCV快速入门&#xff1a;初探2. OpenCV快速入门&#xff1a;像素操作和图像变换3. OpenCV快速入门&#xff1a;绘制图形、图像金字塔和感兴趣区域4. OpenCV快速入门&#xff1a;图像滤波与边缘检测5. OpenCV快速入门&#xff1a;图像形态学操作6. OpenC…

深度学习18

卷积层 查看每个数据 使用tensorboard查看 池化层 使用数据集进行训练 创建实例&#xff0c;使用tensorboard进行显示 最大池化保留了图片信息&#xff0c;神经网络训练的数据量大大减小&#xff0c;可以加快训练 非线性激活 非线性激活为神经网络加入了一些非线性的特质…

ChinaSoft 展商风采 | 蚂蚁集团:CodeFuse-Query代码大数据分析平台

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

Java---类的继承

文章目录 1. 理解继承2. 继承概述3. 代码块理解4. 继承的好处与弊端5. 继承中变量的访问特点6. super关键字7. 继承中构造方法访问特点8. 继承中成员方法访问特点9. 方法重写10. 方法重写注意事项11. Java继承注意事项 1. 理解继承 2. 继承概述 1. 继承是面向对象的三大特征之一…

ILSVRC2012数据集处理

ILSVRC2012数据集处理 解压图像处理标签和图像 解压图像 先解压 tar -xvf ILSVRC2012_img_train.tar 解压之后其实还是1000个tar压缩包&#xff08;对应1000个类别&#xff09;&#xff0c;需要再次解压&#xff0c;解压脚本unzip.sh如下&#xff08;PS&#xff1a;可能需要自…

银行合规知识竞赛要怎么策划才高大上

合规是银行业务永恒的主题&#xff0c;也是银行发展的根本保障。加强合规知识的学习和理解是保障银行业务健康发展的基础。通过竞赛形式的开展&#xff0c;旨在增强员工对风险和合规的敏感度和关注度&#xff0c;推动全行合规水平全面提升。那么如何策划一场高水平的银行合规知…

开发环境配置整理大全——Visual Studio 最新安装篇

Visual Studio是微软的集成开发环境&#xff08;IDE&#xff09;&#xff0c;以Windows为主的平台开发的一套功能全面而强大的IDE&#xff0c;支持C#、F#、VB、C/C、HTML等36 种语言的开发。开发人员常使用的开发工具之一&#xff0c;Visual Studio今年4月出了最新版本的2022款…