JAVA深化篇_32—— 线程使用之线程同步synchronized语法结构【附有详细说明及代码】

线程同步

什么是线程同步

同步问题的提出

现实生活中,我们会遇到“同一个资源,多个人都想使用”的问题。 比如:教室里,只有一台电脑,多个人都想使用。天然的解决办法就是,在电脑旁边,大家排队。前一人使用完后,后一人再使用。

线程同步的概念

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。 这时候,我们就需要用到“线程同步”。 线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

线程冲突案例演示

我们以银行取款经典案例来演示线程冲突现象。

银行取钱的基本流程基本上可以分为如下几个步骤。

(1)用户输入账户、密码,系统判断用户的账户、密码是否匹配。

(2)用户输入取款金额

(3)系统判断账户余额是否大于或等于取款金额

(4)如果余额大于或等于取款金额,则取钱成功;如果余额小于取款金额,则取钱失败。

/*** 账户类*/
class Account{//账号private String accountNo;//账户的余额private double balance;public Account() {}public Account(String accountNo, double balance) {this.accountNo = accountNo;this.balance = balance;}public String getAccountNo() {return accountNo;}public void setAccountNo(String accountNo) {this.accountNo = accountNo;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}
}
/*** 取款线程*/
class DrawThread implements Runnable{//账户对象private Account account;//取款金额private double drawMoney;public DrawThread(Account account,double drawMoney){this.account = account;this.drawMoney = drawMoney;}/*** 取款线程*/@Overridepublic void run() {//判断当前账户余额是否大于或等于取款金额if(this.account.getBalance() >= this.drawMoney){System.out.println(Thread.currentThread().getName()+" 取钱成功!吐出钞票:"+this.drawMoney);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//更新账户余额this.account.setBalance(this.account.getBalance()- this.drawMoney);System.out.println("\t 余额为:"+this.account.getBalance());}else{System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");}}
}public class TestDrawMoneyThread {public static void main(String[] args) {Account account = new Account("1234",1000);new Thread(new DrawThread(account,800),"老公").start();new Thread(new DrawThread(account,800),"老婆").start();}
}

实现线程同步

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问造成的这种问题。这套机制就是synchronized关键字。

synchronized语法结构:

synchronized(锁对象){   同步代码  
}

synchronized关键字使用时需要考虑的问题:

  • 需要对那部分的代码在执行时具有线程互斥的能力(线程互斥:并行变串行)。
  • 需要对哪些线程中的代码具有互斥能力(通过synchronized锁对象来决定)。

它包括两种用法:

synchronized 方法和 synchronized 块。

  • synchronized 方法

    通过在方法声明中加入 synchronized关键字来声明,语法如下:

public synchronized void accessVal(int newVal);
  • synchronized 在方法声明时使用:放在访问控制符(public)之前或之后。这时同一个对象下synchronized方法在多线程中执行时,该方法是同步的,即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。

  • synchronized块

    synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

    Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。

修改线程冲突案例演示

/*** 账户类*/
class Account{//账号private String accountNO;//账户余额private double balance;public Account() {}public Account(String accountNO, double balance) {this.accountNO = accountNO;this.balance = balance;}public String getAccountNO() {return accountNO;}public void setAccountNO(String accountNO) {this.accountNO = accountNO;}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}
}
/*** 取款线程*/
class DrawThread implements Runnable{//账户对象private Account account;//取款金额private double drawMoney;public DrawThread(){}public DrawThread(Account account,double drawMoney){this.account = account;this.drawMoney = drawMoney;}/*** 取款线程体*/@Overridepublic void run() {synchronized (this.account){//判断当前账户余额是否大于或等于取款金额if(this.account.getBalance() >= this.drawMoney){System.out.println(Thread.currentThread().getName()+" 取钱成功!突出钞票"+this.drawMoney);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//更新账户余额this.account.setBalance(this.account.getBalance() - this.drawMoney);System.out.println("\t 余额为:"+this.account.getBalance());}else{System.out.println(Thread.currentThread().getName()+" 取钱失败,余额不足");}}}
}
public class TestDrawMoneyThread {public static void main(String[] args) {Account account = new Account("1234",1000);new Thread(new DrawThread(account,800),"老公").start();new Thread(new DrawThread(account,800),"老婆").start();}
}

线程同步的使用

使用this作为线程对象锁

语法结构:

synchronized(this){ //同步代码 }

public synchronized void accessVal(int newVal){//同步代码}
/*** 定义程序员类*/
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized public void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedException e) {e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedException e) {e.printStackTrace();}}
}/*** 打开电脑的工作线程*/
class Working1 extends Thread{private Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}/*** 编写代码的工作线程*/
class Working2 extends Thread{private Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}
public class TestSyncThread {public static void main(String[] args) {Programmer p = new Programmer("张三");new Working1(p).start();new Working2(p).start();}
}

使用字符串作为线程对象锁

语法结构:

synchronized(“字符串”){ //同步代码 }
/*** 定义程序员类*/
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized public void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedException e) {e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedException e) {e.printStackTrace();}}/*** 去卫生间*/public void wc(){synchronized ("suibian") {try {System.out.println(this.name + " 打开卫生间门");Thread.sleep(500);System.out.println(this.name + " 开始排泄");Thread.sleep(500);System.out.println(this.name + " 冲水");Thread.sleep(500);System.out.println(this.name + " 离开卫生间");} catch (InterruptedException e) {e.printStackTrace();}}}
}/*** 打开电脑的工作线程*/
class Working1 extends Thread{private Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}/*** 编写代码的工作线程*/
class Working2 extends Thread{private Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}/*** 去卫生间的线程*/
class WC extends Thread{private Programmer p;public WC(Programmer p){this.p = p;}@Overridepublic void run() {this.p.wc();}
}
public class TestSyncThread {public static void main(String[] args) {Programmer p = new Programmer("张三");Programmer p1 = new Programmer("李四");Programmer p2 = new Programmer("王五");new WC(p).start();new WC(p1).start();new WC(p2).start();}
}

用Class作为线程对象锁

语法结构:

synchronized(XX.class){ //同步代码 }

synchronized public static void accessVal()
/*** 定义销售员工类*/
class Sale{private String name;public Sale(String name){this.name = name;}/*** 领取奖金*/synchronized public static void money(){try {System.out.println(Thread.currentThread().getName() + " 被领导表扬");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 拿钱");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");} catch (InterruptedException e) {e.printStackTrace();}}
}
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized public void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedException e) {e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedException e) {e.printStackTrace();}}/*** 去卫生间*/public void wc(){synchronized ("suibian") {try {System.out.println(this.name + " 打开卫生间门");Thread.sleep(500);System.out.println(this.name + " 开始排泄");Thread.sleep(500);System.out.println(this.name + " 冲水");Thread.sleep(500);System.out.println(this.name + " 离开卫生间");} catch (InterruptedException e) {e.printStackTrace();}}}/*** 领取奖金*/public void money(){synchronized (Programmer.class) {try {System.out.println(this.name + " 被领导表扬");Thread.sleep(500);System.out.println(this.name + " 拿钱");Thread.sleep(500);System.out.println(this.name + " 对公司表示感谢");Thread.sleep(500);System.out.println(this.name + " 开开心心的拿钱走人");} catch (InterruptedException e) {e.printStackTrace();}}}
}/*** 打开电脑的工作线程*/
class Working1 extends Thread{private Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}/*** 编写代码的工作线程*/
class Working2 extends Thread{private Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}/*** 去卫生间的线程*/
class WC extends Thread{private Programmer p;public WC(Programmer p){this.p = p;}@Overridepublic void run() {this.p.wc();}
}/*** 程序员领取奖金*/
class ProgrammerMoney extends Thread{private Programmer p;public ProgrammerMoney(Programmer p){this.p = p;}@Overridepublic void run() {this.p.money();}
}/*** 销售部门领取奖金*/
class SaleMoney extends Thread{private Sale p;public SaleMoneyThread(Sale p){this.p = p;}@Overridepublic void run() {this.p.money();}
}public class TestSyncThread {public static void main(String[] args) {/*  Programmer p = new Programmer("张三");Programmer p1 = new Programmer("李四");new ProgrammerMoney(p).start();new ProgrammerMoney(p1).start();*/Sale s = new Sale("张晓丽");Sale s1 = new Sale("王晓红");new SaleMoney(s).start();new SaleMoney(s1).start();}
}

使用自定义对象作为线程对象锁

语法结构:

synchronized(自定义对象){ //同步代码 
}
/*** 定义销售员工类*/
class Sale{private String name;public Sale(String name){this.name = name;}/*** 领取奖金*/synchronized public static void money(){try {System.out.println(Thread.currentThread().getName() + " 被领导表扬");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 拿钱");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 对公司表示感谢");Thread.sleep(500);System.out.println(Thread.currentThread().getName() + " 开开心心的拿钱走人");} catch (InterruptedException e) {e.printStackTrace();}}
}
class Programmer{private String name;public Programmer(String name){this.name = name;}/*** 打开电脑*/synchronized public void computer(){try {System.out.println(this.name + " 接通电源");Thread.sleep(500);System.out.println(this.name + " 按开机按键");Thread.sleep(500);System.out.println(this.name + " 系统启动中");Thread.sleep(500);System.out.println(this.name + " 系统启动成功");} catch (InterruptedException e) {e.printStackTrace();}}/*** 编码*/synchronized public void coding(){try {System.out.println(this.name + " 双击Idea");Thread.sleep(500);System.out.println(this.name + " Idea启动完毕");Thread.sleep(500);System.out.println(this.name + " 开开心心的写代码");} catch (InterruptedException e) {e.printStackTrace();}}/*** 去卫生间*/public void wc(){synchronized ("suibian") {try {System.out.println(this.name + " 打开卫生间门");Thread.sleep(500);System.out.println(this.name + " 开始排泄");Thread.sleep(500);System.out.println(this.name + " 冲水");Thread.sleep(500);System.out.println(this.name + " 离开卫生间");} catch (InterruptedException e) {e.printStackTrace();}}}/*** 领取奖金*/public void money(){synchronized (Programmer.class) {try {System.out.println(this.name + " 被领导表扬");Thread.sleep(500);System.out.println(this.name + " 拿钱");Thread.sleep(500);System.out.println(this.name + " 对公司表示感谢");Thread.sleep(500);System.out.println(this.name + " 开开心心的拿钱走人");} catch (InterruptedException e) {e.printStackTrace();}}}
}
class Manager{private String name;public Manager(String name){this.name = name;}public String getName(){return this.name;}/*** 敬酒*/public void cheers(String mName,String eName){try {System.out.println(mName + " 来到 " + eName + " 面前");Thread.sleep(500);System.out.println(eName + " 拿起酒杯");Thread.sleep(500);System.out.println(mName + " 和 " + eName + " 干杯");} catch (InterruptedException e) {e.printStackTrace();}}
}
/*** 打开电脑的工作线程*/
class Working1 extends Thread{private Programmer p;public Working1(Programmer p){this.p = p;}@Overridepublic void run() {this.p.computer();}
}/*** 编写代码的工作线程*/
class Working2 extends Thread{private Programmer p;public Working2(Programmer p){this.p = p;}@Overridepublic void run() {this.p.coding();}
}/*** 去卫生间的线程*/
class WC extends Thread{private Programmer p;public WC(Programmer p){this.p = p;}@Overridepublic void run() {this.p.wc();}
}/*** 程序员领取奖金*/
class ProgrammerMoney extends Thread{private Programmer p;public ProgrammerMoney(Programmer p){this.p = p;}@Overridepublic void run() {this.p.money();}
}/*** 销售部门领取奖金*/
class SaleMoneyThread extends Thread{private Sale p;public SaleMoneyThread(Sale p){this.p = p;}@Overridepublic void run() {this.p.money();}
}/*** 敬酒线程类*/
class CheersThread extends Thread{private Manager manager;private String name;public CheersThread(String name,Manager manager){this.name = name;this.manager = manager;}@Overridepublic void run() {synchronized (this.manager) {this.manager.cheers(this.manager.getName(), name);}}
}public class TestSyncThread {public static void main(String[] args) {Manager manager = new Manager("张三丰");new CheersThread("张三",manager).start();new CheersThread("李四",manager).start();}
}

死锁及解决方案

死锁的概念

“死锁”指的是:

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。

某一个同步块需要同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。比如,“化妆线程”需要同时拥有“镜子对象”、“口红对象”才能运行同步块。那么,实际运行时,“小丫的化妆线程”拥有了“镜子对象”,“大丫的化妆线程”拥有了“口红对象”,都在互相等待对方释放资源,才能化妆。这样,两个线程就形成了互相等待,无法继续运行的“死锁状态”。

死锁案例演示

/*** 口红类*/
class Lipstick{}/*** 镜子类*/
class Mirror{}/*** 化妆线程类*/
class Makeup extends Thread{private int flag; //flag=0:拿着口红。flag!=0:拿着镜子private String girlName;static Lipstick lipstick = new Lipstick();static Mirror mirror = new Mirror();public Makeup(int flag,String girlName){this.flag = flag;this.girlName = girlName;}@Overridepublic void run() {this.doMakeup();}/*** 开始化妆*/public void doMakeup(){if(flag == 0){synchronized (lipstick){System.out.println(this.girlName+" 拿着口红");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (mirror){System.out.println(this.girlName+" 拿着镜子");}}}else{synchronized (mirror){System.out.println(this.girlName+" 拿着镜子");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lipstick){System.out.println(this.girlName+" 拿着口红");}}}}
}public class DeadLockThread {public static void main(String[] args) {new Makeup(0,"大丫").start();new Makeup(1,"小丫").start();}
}

死锁问题的解决

死锁是由于 “同步块需要同时持有多个对象锁造成”的,要解决这个问题,思路很简单,就是:同一个代码块,不要同时持有两个对象锁。

/*** 口红类*/
class Lipstick{}/*** 镜子类*/
class Mirror{}/*** 化妆线程类*/
class Makeup extends Thread{private int flag; //flag=0:拿着口红。flag!=0:拿着镜子private String girlName;static Lipstick lipstick = new Lipstick();static Mirror mirror = new Mirror();public void setFlag(int flag) {this.flag = flag;}public void setGirlName(String girlName) {this.girlName = girlName;}@Overridepublic void run() {this.doMakeup();}/*** 开始化妆*/public void doMakeup(){if(flag == 0){synchronized (lipstick){System.out.println(this.girlName+" 拿着口红");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}synchronized (mirror){System.out.println(this.girlName+" 拿着镜子");}}else{synchronized (mirror){System.out.println(this.girlName+" 拿着镜子");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}synchronized (lipstick){System.out.println(this.girlName+" 拿着口红");}}}
}public class DeadLockThread {public static void main(String[] args) {Makeup makeup = new Makeup();makeup.setFlag(0);makeup.setGirlName("大丫");Makeup makeup1 = new Makeup();makeup1.setFlag(1);makeup1.setGirlName("小丫");makeup.start();makeup1.start();}
}

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

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

相关文章

[自动化运维工具] Ansible的简单介绍与常用模块详解

文章目录 1. Ansible概述1.1 简介1.2 Ansible的特性1.3 Ansible的组件构成1.4 Ansible的工作原理 2. Ansible环境部署2.1 前置准备2.2 安装ansible2.3 查看基本信息2.4 配置远程主机清单 3. Ansible的常用模块3.1 ansible的基础命令格式3.2 Command模块3.2.1 基本格式和常用参数…

通过51单片机控制28byj48步进电机按角度正反转旋转

一、前言 本项目基于STC89C52单片机,通过控制28BYJ-48步进电机实现按角度正反转旋转的功能。28BYJ-48步进电机是一种常用的电机,精准定位和高扭矩输出,适用于许多小型的自动化系统和机械装置。 在这个项目中,使用STC89C52单片机…

跟着Nature Communications学作图:纹理柱状图+添加显著性标签!

📋文章目录 复现图片设置工作路径和加载相关R包读取数据集数据可视化计算均值和标准差方差分析组间t-test 图a可视化过程图b可视化过程合并图ab 跟着「Nature Communications」学作图,今天主要通过复刻NC文章中的一张主图来巩固先前分享过的知识点&#…

Git同时配置Gitee和GitHub

Git同时配置Gitee和GitHub 一、删除原先ssh密钥二、生成密钥 这里的同时配置是针对于之前配置过单个gitee或者github而言的,如果需要看git从安装开始的配置,则可以看这一篇文章 git安装配置教程 一、删除原先ssh密钥 在C盘下用户/用户名/.ssh文件下找到…

ESP32S3入手体验测试

ESP32S3入手体验测试 🔖所入手的型号是YD-ESP32-S3 N16R8,该款和乐鑫官方推出的ESP32-S3-DevKitC-1配置差不多。 🎈乐鑫官方介绍:ESP32-S3-DevKitC-1 v1.1 🔰两者采用的模组:ESP32-S3-WROOM-1 和ESP32-S3-WROOM-1U模组…

“Java与Redis的默契舞曲:优雅地连接与存储数据“

文章目录 引言1. Java连接上Redis2. Java对Redis进行存储数据2.1 存储set类型数据2.2 存储hash类型数据2.3 存储list类型数据 总结 引言 在现代软件开发中,数据存储和处理是至关重要的一环。Java作为一门强大的编程语言,与Redis这个高性能的内存数据库相…

2023-11 | 短视频批量下载/爬取某个用户的所有视频 | Python

这里以鞠婧祎的个人主页为demo https://www.douyin.com/user/MS4wLjABAAAACV5Em110SiusElwKlIpUd-MRSi8rBYyg0NfpPrqZmykHY8wLPQ8O4pv3wPL6A-oz 【2023-11-4 23:02:52 星期六】可能后面随着XX的调整, 方法不再适用, 请注意 找到接口 找到https://www.douyin.com/aweme/v1/web/…

C++ map 的使用

下面的是关于 map 的介绍。来自 map - C Reference (cplusplus.com) 的翻译,您可以看也可以不看哈! map 是关联容器,它按照特定的次序(按照 key 来比较)存储由键值 key 和值 value组合而成的元素。在 map 中,键值 key 通常用于排序…

P2824 [HEOI2016/TJOI2016] 排序

P2824 [HEOI2016/TJOI2016] 排序 [HEOI2016/TJOI2016] 排序 题目描述 在 2016 2016 2016 年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。 这个难题是这样子的&#x…

linux 创建git项目并提交到gitee(保姆式教程)

01、git安装与初始化设置 mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ apt install mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.name "用户名" mhzzjmhzzj-virtual-machine:~/work/skynetStudy$ git config --global user.ema…

Python自定义函数练习(持续更新中~)

1.计算矩阵的面积和周长: class Rectangle:def __init__(self, width, height):self.width widthself.height heightdef area(self):return self.width * self.heightdef perimeter(self):return 2 * (self.width self.height)if __name__ "__main__"…

深入理解JS中的this

1、浅谈this 1.1、调用位置 在学习this的绑定过程之前,首先要理解调用位置,即函数在代码中被调用的位置,因此我们需要分析调用栈,看以下代码 function baz(){// 当前调用栈是baz// 因此调用位置就是全局作用域console。log(&qu…

财务RPA机器人如何使用

随着科技的不断发展,自动化技术在各个领域得到了广泛应用,在财务领域,RPA机器人已经成为一种新兴的技术手段,帮助众多企业实现了财务流程的自动化,大大提高了工作效率,降低人力成本。 本文将详细介绍财务RP…

AtCoder Beginner Contest 327 G. Many Good Tuple Problems(带标号二分图计数+有区别小球放入有区别盒子)

题目 一个长为n(n<30)的原始序列x&#xff0c;x[i]可以取值0或1 一个长为m(m<1e9)的点对序列(s,t)&#xff0c; s序列第i项和t的第i项&#xff0c;均可以取值[1,n]&#xff0c; 如果构造好s和t后&#xff0c;对任意都存在01序列x使得&#xff0c; 则称这个序列是合法…

【RabbitMQ】 RabbitMQ 消息的延迟 —— 深入探索 RabbitMQ 的死信交换机,消息的 TTL 以及延迟队列

文章目录 一、死信交换机1.1 什么是死信和死信交换机1.2 死信交换机和死信队列的创建方式 二、消息的 TTL2.1 什么是消息的 TTL2.2 基于死信交换机和 TTL 实现消息的延迟 三、基于 DelayExchang 插件实现延迟队列3.1 安装 DelayExchang 插件3.2 DelayExchang 实现消息延迟的原理…

MySQL5.7小版本升级-In-Place方式

在 Centos7.9上升级二进制方式安装的MySQL 。升级方式有就地升级和逻辑升级2种方法&#xff0c;本实验演示In-Place Upgrade就地升级的方式将mysql-5.7.17升级至5.7.44 In-Place Upgrade就地升级Logical Upgrade逻辑升级 升级方式介绍 In-Place Upgrade就地升级 In-Place U…

perl列表创建、追加、删除

简介 perl 列表追加元素 主要是通过push和unshift函数来实现。其中&#xff0c;push是追加到列表尾&#xff0c;unshift是追加到列表头。 perl列表删除元素 主要是通过pop和shift函数来实现。其中&#xff0c;pop是从列表尾删除一个元素&#xff0c; shift是从列表头删除一…

6大场景,玩转ChatGPT!

文章目录 一、故事叙述提问举例 二、产品描述提问举例 三、报告撰写提问举例 四、邮件和信件撰写提问举例 五、新间稿和公告撰写提问举例 六、学术论文和专业文章撰写提问举例 本文是在GPT3.5版本下演示的 我们知道AI技术不仅能够自动生成文章和内容&#xff0c;还可以根据我们…

【大数据】NiFi 中的重要术语

NiFi 中的重要术语 1.Flow Controller2.Processor3.Connection4.Controller Service5.Process Group6.FlowFile 那些一个个黑匣子称为 Processor&#xff0c;它们通过称为 Connection 的队列交换名为 FlowFile 的信息块。最后&#xff0c;FlowFile Controller 负责管理这些组件…

软考高级系统架构设计师系列之:软考高级系统架构设计师论文专题

软考高级系统架构设计师系列之:软考高级系统架构设计师论文专题 一、论文相关内容二、论文专题大纲三、论文考试方式四、历年真题汇总分析五、论文常见问题六、论文评分标准七、搭建论文万能模版八、论文万能模版公式九、搭建论文万能模版—摘要十、搭建论文万能模版—背景十一…