二十章多线程

news2024/11/17 1:39:17

概念

有很多工作是可以同时完成的,这种思想放在Java中被称为并发,并发完成每一件事被称为线程。

程序员可以在程序中执行多个线程,每一个线程完成一个功能//与其他线程并发执行,这种机制被称为多线程,并不算所有编程语言都支持多线程。

创建线程

继承Thread类和实现Runnable接口两种方法

继承Thread类

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

public Thread():创建一个新的线程对象

public Thread(String threadName):创建一个名为threadName的线程对象

继承Thread类创建一个新的线程的语法如下:

public class ThreadTest extends Thread{

}

完成线程真实代码的功能放在run()方法,当继承Thread类后,就可以在该线程中覆盖run()方法,将实现线程功能的代码写入run()方法中,调用run()方法。

Thread对象需要一个任务来执行,任务是指线程在启动时执行的工作,这个代码写在了run()方法中,语法格式如下:

public void run(){

如果start()方法调用一个已经启动的线程,系统将抛出IllegalThreadStateException异常

例题20.1

package lx;
 
public class Demo20_1 extends Thread {
	public void run(){
		for(int i=0;i<=10;i++) {
			System.out.println(i+"");
		}
	}
	public static void main(String[] args) {
		Demo20_1 th=new Demo20_1();
		th.start();
		
	}
 
}
 

结果

 

实现Runnable接口

线程都是通过扩展 Thread 类来创建的,如果需要继承其他类(非 Thread类),而且还要使当前类实现多线程,那么可以通过 Runnable 接口来实现。实现 Runnable 接口的语法如下:

public class Thread extends Object implements Runnable{
}

 实质上 Thread 类实现了 Runnable 接口,其中的run()方法正是对 Runnable 接口中的 run()方法的具体实现

实现 Runnable 接口的程序会创建一个 Thread 对象,并将 Runnable 对象与 Thread 对象相关联。

Thread 类中有两个构造方法:

public Thread(Runnable target)。

public Thread(Runnable target,String name)。

这两个构造方法的参数中都存在 Runnable 实例

使用 Runnable 接口启动新的线程的步骤如下:

建立 Runnable 对象。

使用参数为 Runnable 对象的构造方法创建 Thread 实例。

调用 start()方法启动线程。

 

 线程最引人注目的部分应该是与 Swing 相结合创建GUI程序

例题20.2

package lx;
 
import java.awt.Container;
 
import javax.swing.JFrame;
import javax.swing.*;
 
public class Demo20_2 extends JFrame {
	int c=0;//图标横坐标
	public Demo20_2() {
		setBounds(300,200,250,100);//绝对定位窗体大小和位置
		Container con=getContentPane();//主容器
		con.setLayout(null);//使窗体不使用任何布局管理器
		
		Icon img=new ImageIcon("src/1.gif");//图标对象
		JLabel jl=new JLabel(img);//显示图标的标签
		jl.setBounds(10, 10, 200, 50);//设置标签的位置和大小
		Thread t=new Thread() {//定义匿名线程对象
			public void run() {
				while(true) {
					jl.setBounds(c, 10, 200, 50);//将标签的横坐标用线程表示
					try {
						Thread.sleep(500);//使线程休眠500毫秒
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					c+=4;//使横坐标每次增加4
					if(c>=120) {
						c=10;//当图标到达标签最右边时,使其回到标签最左边
					}
				}
			}
		};
		t.start();//启动线程
		con.add(jl);//将标签添加到容器中
		setVisible(true);//使窗体可见
		setDefaultCloseOperation(EXIT_ON_CLOSE);//设置窗体的关闭方式
	}
	public static void main(String[] args) {
		new Demo20_2();
	}
 
}

结果

 

 

为了使图标具有滚动功能,需要在类的构造方法中创建 Thread 实例。

在创建该实例的同时需要 Runnable 对象作为 Thread 类构造方法的参数,然后使用内部类形式实现 run()方法。

在 run()方法中主要循环图标的横坐标位置,

当图标横坐标到达标签的最右方时,再次将图标的横坐标置于图标滚动的初始位置。
启动一个新的线程,不是直接调用 Thread 子类对象的 run()方法,而是调用 Thread 子类的 start()方法,Thread 类的 start()方法产生一个新的线程,该线程运行 Thread 子类的 run()方法。

 

 线程的生命周期

线程具有生命周期,其中包含 7 种状态,分别为出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态。

出生状态就是线程被创建时处于的状态,在用户使用该线程实例调用start()方法之前线程都处于出生状态.

当用户调用 start()方法后,线程处于就绪状态 

当线程得到系统资源后就进入运行状态。

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

虽然多线程看起来像同时执行,但事实上在同一时间点上只有一个线程被执行,只是线程之间切换较快,所以才会使人产生线程是同时进行的假象。

操作线程的方法

操作线程有很多方法,这些方法可以使线程从某一种状态过波到另一种状态。

 线程的休眠

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

try{
Thread.sleep(2000);

}catch(InterruptedException e){
e.printStackTrace();

}

上述代码会使线程在 2 秒之内不会进入就绪状态。

由于 sleep()方法的执行有可能抛InterrupledException 异常,所以将 sleep()方法的调用放在 try-catch 块中。

不能保证线程醒来后进入运行状态,只能保证它进入就绪状态。
 

 例题20.3

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

结果

 

在本实例中定义了 getC()方法,该方法用于随机产生 Color 类型的对象并且在产生线程的匿名内部类中使用 getGraphics()方法获取 Graphics 对象,使用该对象调用 setColor()方法为图形设置颜色。调用 drawLine()方法绘制一条线段,同时线段会根据纵坐标的变化自动调整。
 

线程的加入

如果当前某程序为多线程程序,假如存在一个线程 A,现在需要插入线程 B,并要求线程 B 先执行完毕,然后再继续执行线程 A,此时可以使用 Thread 类中的 join()方法来完成。

例题20.4

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

结果

 

线程的中断

如果线程是因为使用了 sleep()或 wait()方法进入了就绪状态,可以使用 Thread 类中 interrupt()方法使线程离开run()方法,同时结束线程,但程序会抛出 InterruptedException 异常,用户可以在处理该异常时完成线程的中断业务处理,如终止 while 循环。
例题20.5

package lx;
 
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 Demo20_5 extends JFrame{
	
	public Demo20_5(){
		JProgressBar Bar=new JProgressBar();//创建进度条
		getContentPane().add(Bar,BorderLayout.NORTH);//将进度条设置在窗体最北面
		JButton b=new JButton("停止");
		getContentPane().add(b,BorderLayout.SOUTH);//将进度条设置在窗体最南面
		Bar.setStringPainted(true);//设置进度条显示数字字符
		Thread t=new Thread(new Runnable() {
			int c=0;
			public void run() {
				while(true) {
					Bar.setValue(++c);//设置进度条当前值
					try {
						Thread.sleep(100);//让A线程休眠100毫秒
					}catch(InterruptedException e) {//捕捉InterruptedException异常
						System.out.println("当前线程程序被中断");
						break;
					}
				}
			}
			
		});
		b.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				t.interrupt();//中断线程
			}
		});
		t.start();//启动线程
	}
	public static void init(JFrame frame,int w,int h) {
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setSize(w, h);
		frame.setVisible(true);
	}
	public static void main(String[] args) {
		init(new Demo20_5(),100,100);
 
	}
 
}

结果

 

线程的礼让

Thread 类中提供了一种礼让方法,使用 yield()方法表示,它只是给当前正处于运行状态的线程一个提醒,告知它可以将资源礼让给其他线程,但这仅是一种暗示,没有任何一种机制保证当前线程会将资源礼让。yicld()方法使具有同样优先级的线程有进入可执行状态的机会,在当前线程放弃执行权时会再度回到就绪状态。

线程的优先级

每个线程都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性。

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

例题20.6
package lx;
 
public class Demo20_6 implements Runnable{
	String name;
	public 	Demo20_6(String name) {
		this.name=name;
	}
		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 Demo20_6("A"));//A线程优先级最小
	a.setPriority(1);
	Thread b=new Thread(new Demo20_6("B"));
	b.setPriority(3);
	Thread c=new Thread(new Demo20_6("C"));
	c.setPriority(7);
	Thread d=new Thread(new Demo20_6("D"));//D线程优先级最大
	d.setPriority(10);
	a.start();
	b.start();
	c.start();
	d.start();
	//线程的执行顺序由CPU决定,所有可能不一定按优先级排序
	}
 
}

结果

 

由于线程的执行顺序是由 CPU 决定的,即使线程设定了优先级也是作为 CPU 的参考数据,所以真实的运行结果可能并不一定按照优先级排序

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

 线程安全
实际开发中,使用多线程程序的情况很多,以火车站售票系统为例,在代码中判断当前票数是否大于 0,如果大于 0 则执行将该票出售给乘客的功能,但当两个线程同时访问这段代码时(假如这时只剩下一张票),第一个线程将票售出,与此同时第二个线程也已经执行完成判断是否有票的操作,并得出票数大于 0 的结论,于是它也执行售出操作,这样就会产生负数。

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

实例
 

package lx;
 
public class Demo20_6_1 implements Runnable {
	int n=10;//设置当前总票数
	public void run() {
		while(true) {//设置无限循环
			if(n>0) {//判断当前票数是否大于 0
				try {
					Thread.sleep(100);		//使当前线程休眠 100毫秒	
					}
			catch(InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"————票数"+n--);//票数减1
			}	
		}
	}
	
	
	public static void main(String[] args) {
		Demo20_6_1 t=new Demo20_6_1();//实例化类对象
		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();
		
		
	}
 
}

结果 

 

线程同步机制
该如何解决资源共享的问题呢? 所有解决多线程资源冲突问题的方法基本上都是采用给定时间只允许一个线程访问共享资源的方法,这时就需要给共享资源上一道锁。

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

synchronized (Object){

}

 通常将共享资源的操作放置在 synchronized 定义的区域内,这样当其他线程获取到这个锁时,就必须等待锁被释放后才可以进入该区域。

例题20.7

package lx;
 
public class Demo20_6_1 implements Runnable {
	int n=10;//设置当前总票数
	public void run() {
		while(true) {//设置无限循环
			synchronized (this) {
				if(n>0) {//判断当前票数是否大于 0
					try {
						Thread.sleep(100);		//使当前线程休眠 100毫秒	
						}
				catch(InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"————票数"+n--);//票数减1
				}	
			}
			
		}
	}
	
	
	public static void main(String[] args) {
		Demo20_6_1 t=new Demo20_6_1();//实例化类对象
		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();
		
		
	}
 
}

结果 

 

从这个结果可以看出,打印到最后票数没有出现负数,这是因为将共享资源放置在了同步块中,不管程序如何运行都不会出现负数。

同步方法

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

synchronized void f(){

}

 当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的方法修饰为 synchronized,否则就会出错。

修改20.7的代码如下:
package lx;
 
public class Demo20_6_1 implements Runnable {
	int n=10;//设置当前总票数
	
	public  synchronized void du() {
		if(n>0) {//判断当前票数是否大于 0
			try {
				Thread.sleep(100);		//使当前线程休眠 100毫秒	
				}
		catch(InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"————票数"+n--);//票数减1
		}	
	}
	public void run() {
		while(true) {//设置无限循环
			du();
			
		}
	}
	
	
	public static void main(String[] args) {
		Demo20_6_1 t=new Demo20_6_1();//实例化类对象
		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/1262209.html

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

相关文章

Java 之 lambda 表达式(二)---- Stream 操作 API

目录 一. 前言 二. Stream 创建 2.1. 使用集合来创建 Stream 2.2. 使用数组创建 Stream 2.3. 由值创建 Stream 2.4. 由函数创建无限流 Stream 2.5. 代码示例 三. Stream 操作 3.1. 中间型操作 3.1.1. filter() 3.1.2. map() 3.1.3. mapToInt()、mapToLong()、mapTo…

ESP32-Web-Server编程- JS 基础5

ESP32-Web-Server编程- JS 基础5 概述 JS 编程内容颇多&#xff0c;我们提供一些简单的示例&#xff0c;先玩再学&#xff0c;边玩边学。 示例1-演示通过 JS 进行温度转换 资源链接 对应示例的 code 链接 &#xff08;点击直达代码仓库&#xff09; 示例2-增加网页弹窗 演…

【数据结构—单链表的实现】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1. 链表的概念及结构 2. 单链表的实现 2.1单链表头文件——功能函数的定义 2.2单链表源文件——功能函数的实现 2.3 单链表源文件——功能的测试 3.具体的理解操作…

Selenium 学习(0.14)——软件测试之测试用例设计方法——因果图法2【基本步骤及案例】

1、因果图法的基本步骤 2、案例分析 1&#xff09;分析原因和结果 2&#xff09;关联原因和结果 投入1元5角或2元&#xff0c;按下“可乐”&#xff0c;送出“可乐”【暂时忽略找零】 投入2元&#xff0c;按下“可乐”或“雪碧”。找零5角&#xff0c;送出“可乐”或“雪…

vue中keep-alive的使用

什么是keep-alive&#xff1f; keep-alive是一个内置组件&#xff0c;用于缓存和管理组件的状态。 当 keep-alive包裹一个组件时&#xff0c;这个组件的状态将会被缓存起来&#xff0c;而不是每次重新渲染。这在多个视图之间切换时特别有用&#xff0c;可以避免重复的创建和销…

uniapp前端+python后端=微信小程序支付到底怎么开发???国内的资料为什么没一篇能讲清楚,简简单单的只需要3步就可以了-V2版本

一.微信小程序支付 真的&#xff0c;在接到这个任务的时候&#xff0c;本以为很简单&#xff0c;不就是普通的浏览器复制粘贴&#xff0c;最不济找下gpt给生成一下&#xff0c;但是到实际开发就不同了&#xff0c;不是后端出问题就是前端&#xff0c;搜资料&#xff0c;上百度…

【Rust】基本的语法概念

Rust初学习 常见概念变量与可变性变量常量隐藏 数据类型标量类型字符类型复合类型元组数组 函数参数语句和表达式具有返回值的函数 注释控制流使用循环重复执行 常见概念 变量与可变性 变量 fn main() {let x 5;println!("The value of x is: {x}");x 6;println…

AlphaFold的原理及解读

1、背景 蛋白质是生物体内一类重要的生物大分子&#xff0c;其结构复杂多样&#xff0c;蛋白质的结构对于理解其功能和参与的生物学过程具有重要意义。从生物学角度上看&#xff0c;蛋白质的结构可以分为四个层次&#xff1a;初级结构、二级结构、三级结构和四级结构。 初级结…

中英双语大模型ChatGLM论文阅读笔记

论文传送门&#xff1a; [1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling [2] Glm-130b: An open bilingual pre-trained model Github链接&#xff1a; THUDM/ChatGLM-6B 目录 笔记AbstractIntroduction 框架总结1. 模型架构2. 预训练设置3…

MySQL的Linux安装

在MySQL官网下载压缩包MySQL :: Download MySQL Community Server (Archived Versions) 下载完成后将压缩包上传到Linux中。我这里是下的CentOS的压缩包。 并且用的是FinalShell连接工具&#xff0c;可以选择压缩包直接上传。 ​ 上传完毕后&#xff0c;新建mysql文件夹&…

[DASCTF 2023 0X401七月暑期挑战赛] web刷题记录

文章目录 EzFlask方法一 python原型链污染方法二 flask框架静态文件方法三 pin码计算 MyPicDisk方法一 字符串拼接执行命令方法二 phar反序列化 EzFlask 考点&#xff1a;python原型链污染、flask框架理解、pin码计算 源码如下 import uuidfrom flask import Flask, request, …

Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/134561660 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

OpenVINO异步Stable Diffusion推理优化方案

文章目录 Stable Diffusion 推理优化背景技术讲解&#xff1a;异步优化方案思路&#xff1a;异步推理优化原理OpenVINO异步推理Python API同步和异步实现方式对比 oneflow分布式调度优化优势&#xff1a;实现思路 总结&#xff1a; Stable Diffusion 推理优化 背景 2022年&am…

山西电力市场日前价格预测【2023-11-29】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-11-29&#xff09;山西电力市场全天平均日前电价为275.28元/MWh。其中&#xff0c;最高日前电价为415.78元/MWh&#xff0c;预计出现在17:45。最低日前电价为0.00元/MWh&#xff0c;预计出…

Make Pixels Dance: High-Dynamic Video Generation论文解析

高动态视频生成的新进展 Make Pixels Dance: High-Dynamic Video Generation高动态视频生成的新进展前言视频生成模式摘要论文十问实验数据集定量评估指标消融研究 训练和推理技巧训练技术推理技术 更多的应用 Make Pixels Dance: High-Dynamic Video Generation 高动态视频生…

MySQL在Docker容器中的性能损失分析与优化策略

文章目录 1. Docker容器对MySQL性能的潜在影响1.1. IO性能1.2. 网络性能1.3. 资源隔离 2. 优化策略2.1. 使用本地数据卷2.2. 配置合理的容器网络2.3. 限制容器资源2.4. 使用容器编排工具 3. 性能测试与监控4. 结论 &#x1f389;MySQL在Docker容器中的性能损失分析与优化策略 ☆…

sqli-labs靶场详解(less17-less22)

目录 less-17 less-18 less-19 less-20 less-21 less-22 less-17 修改密码关卡 服务器后端 账号密码都存在数据库中 使用UPDATE进行修改密码 尝试username处 尝试好久尝试不出来应该是对用户名进行了过滤 于是对password进行注入 判断注入点 passwdadmin 报错&#xff1a…

MySQL使用函数和存储过程实现:向数据表快速插入大量测试数据

实现过程 1.创建表 CREATE TABLE user_info (id INT(11) NOT NULL AUTO_INCREMENT,name VARCHAR(20) DEFAULT NULL,age INT(3) DEFAULT NULL,pwd VARCHAR(20) DEFAULT NULL,phone_number VARCHAR(11) DEFAULT NULL,email VARCHAR(255) DEFAULT NULL,address VARCHAR(255) DEF…

【TinyALSA全解析(二)】wav和pcm音频文件格式详解

wav和pcm音频文件格式详解 一、本文的目的二、wav和pcm格式文件介绍三、pcm格式文件解析四、wav文件内容解析4.1 文件内容描述4.2 实战分析 五、如何在各种音频格式之间进行转换 /******************************************************************************************…

技术SEO的基础知识和 10 个最佳实践

你有没有想过导致某些网站在搜索结果中排名比其他网站更好的因素&#xff1f;针对搜索引擎进行优化是关键&#xff08;SEO&#xff09;。SEO&#xff0c;即搜索引擎优化&#xff0c;是一种用于提高网站在搜索引擎中的知名度的方法。技术搜索引擎优化&#xff08;SEO&#xff09…