JUC并发编程学习笔记

news2025/2/8 14:16:25

1:回顾多线程

进程和线程是什么
进程是操作系统分配资源的最小单元,而线程是cpu调度的最小单元。

  • java默认有几个线程
    2个,main线程和GC线程(GC垃圾回收机制)

  • java可以开启线程么
    不能

  • 并发和并行
    并发,多线程操作同一个资源,cpu单核,模拟多条线程,快速交替
    并行,多人一起走,cpu多核,多个线程可以同时执行,线程池

package main;
public class Demo1 {
    public static void main(String[] args) {
        //获取cpu的核数
        //cpu密集型,io密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

示例:

线程有几个状态:

Thread.State

    public enum State {
        /**
         * 新建状态
         */
        NEW,

        /**
         * 运行状态
         */
        RUNNABLE,

        /**
         * 堵塞状态
         */
        BLOCKED,

        /**
         * 等待状态
         */
        WAITING,

        /**
         * 超时等待
         */
        TIMED_WAITING,

        /**
         * 终止状态
         */
        TERMINATED;
    }

1.1 wait/sleep 的区别

1.来自不同类,wait->Object,sleep->Thread
2.锁的释放,wait->释放锁,sleep->不释放锁
3.使用范围,wait->同步代码块,sleep->任何地方

1.2 synchronized锁 

package main;
/*
* 真正的多线程开发,公司中的开发,降低耦合型
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1. 属性  方法
* */
public class TicketSale {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源丢入线程
        Ticket ticket = new Ticket();
        //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码}
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类OOP
class  Ticket{
    //属性  方法
    private int number = 30;
    //卖票的方式
    public synchronized void sale(){
        if (number>0){
            System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
        }
    }
}

1.3 Lock 锁

Class ReentrantLock 构造方法
public ReentrantLock()创建一个ReentrantLock的实例。 这相当于使用ReentrantLock(false)
public ReentrantLock(boolean fair)根据给定的公平政策创建一个 ReentrantLock的实例. fair - true如果此锁应使用合理的订购策略

1.3.1 什么是公平锁,非公平锁?

  • 非公平锁:可以插队(无参构造方法默认为非公平锁)
  • 公平锁:先来后到(有参构造方法传值true时为公平锁)
package main;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
 * 真正的多线程开发,公司中的开发,降低耦合型
 * 线程就是一个单独的资源类,没有任何附属的操作!
 * 1. 属性  方法
 * */
public class TicketSale2 {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源丢入线程
        Ticket2 ticket = new Ticket2();
        //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码}
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
/*
* lock三部曲
* 1.new ReentrantLock()
* 2.lock.lock();//加锁
* 3.finally-> lock.unlock() //解锁
* */
class  Ticket2{
    //属性  方法
    private int number = 30;
    //卖票的方式
    Lock lock = new ReentrantLock();
    public  void sale(){
        lock.lock();//加锁
        try {
            //业务代码
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}



/*
公平锁
线程1购买了第10张票
线程2购买了第9张票
线程3购买了第8张票
线程1购买了第7张票
线程2购买了第6张票
线程3购买了第5张票
线程1购买了第4张票
线程2购买了第3张票
线程3购买了第2张票
线程1购买了第1张票
非公平锁
线程1购买了第10张票
线程1购买了第9张票
线程1购买了第8张票
线程1购买了第7张票
线程1购买了第6张票
线程1购买了第5张票
线程1购买了第4张票
线程1购买了第3张票
线程1购买了第2张票
线程1购买了第1张票
*/

1.4  synchronized 和 Lock 区别

  1. lock是一个接口,而synchronized是java的一个关键字。
  2. synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
  3. Synchronized 可重入锁,不可以中断的,非公平; Lock ,可重入锁,可以判断锁,非公平(可以自己设置);
  4. Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
  5. Synchronized 线程1(获得锁,阻塞)、绩程2(等待,傻傻的等) ; Lock锁就不一定会等待下去;
  6. Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁

synchronized

Lock

1.5 synchronized 版的生产者和消费者 

package pc;
/*
* 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒
* 线程交替执行  A  B  操作同一个变量  num = 0;
* A num+1
* B num-1
* */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
//等待 业务 通知
class Data{//数字  资源类
    private  int number = 0;
    public  synchronized void increment() throws InterruptedException {
        if (number != 0) {
            //等待
            this.wait();
        }
        number++;
        //通知其他线程,我+1完毕了
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        if (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        this.notifyAll();
    }
}

1.6 防止虚假唤醒问题

四个线程时(两个生产者两个消费者)出现虚假唤醒问题,需要使用while来替换if来判断唤醒后是否可以继续执行
理解 : 拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行

package pc;
/*
* 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒
* 线程交替执行  A  B  操作同一个变量  num = 0;
* A num+1
* B num-1
* */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
//等待 业务 通知
class Data{//数字  资源类
    private  int number = 0;
    public  synchronized void increment() throws InterruptedException {
        while (number != 0) {
            //等待
            this.wait();
        }
        number++;
        //通知其他线程,我+1完毕了
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"-->"+number);
        this.notifyAll();
    }
}

 1.7 JUC 版的生产者消费者问题 Lock Condition

await:

 signalAll:

package pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
//等待 业务 通知
class Data2 {//数字  资源类
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
//    condition.await();//等待
//    condition.signalAll();//唤醒全部
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            //业务代码
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            //通知其他线程,我+1完毕了
            System.out.println(Thread.currentThread().getName() + "-->" + number);
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "-->" + number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

1.8 Condition 实现精准通知唤醒

await:

 signal:

package pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}
class Data3 {// 资源类 Lock
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1;
    public void printA() {
        lock.lock();
        try {
            //业务,判断->执行->通知
            while (number != 1) {
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "--->A");
            //唤醒指定的人:B
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            //业务,判断->执行->通知
            while (number != 2) {
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "--->B");
            //唤醒指定的人:C
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            //业务,判断->执行->通知
            while (number!=3){
                //等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"--->C");
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

1.9 八锁现象问题理解

package lock;
import java.util.concurrent.TimeUnit;
/*
 * 1.标准情况下,两个线程先打印发短信还是打电话?1.发短信﹑2.打电话
 * 2.sendSms延迟4秒,两个线程先打印 发短信还是 打电话?1.发短信 2.打电话
 * */
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    //synchronized 锁的对象是方法的调用者
    //两个方法是同一个锁,谁先拿到谁先执行
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

结果:


package com.example.juc.test;
import java.util.concurrent.TimeUnit;
/*
 * 3.增加了一个普通方法,先发短信还是Hello
 * 4.两个对象,两个同步方法,先发短信还是先打电话
 * */
public class Demo1 {
   public static void main(String[] args) throws InterruptedException {
      //两个对象,两个调用者,两把锁!
      Phone phone = new Phone();
      Phone phone2 = new Phone();
      //锁的存在
      new Thread(()->{
         System.out.println(Thread.currentThread().getName());
         phone.hello();
         phone.sendSms();
      },"A").start();
      TimeUnit.SECONDS.sleep(1);
      new Thread(()->{
         System.out.println(Thread.currentThread().getName());
         phone2.hello();
         phone2.call();
      },"B").start();
   }
}
class Phone{
   //synchronized 锁的对象是方法的调用者
   //两个方法是同一个锁,谁先拿到谁先执行
   public synchronized void sendSms(){
      try {
         TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println("发短信");
   }
   public synchronized void call(){
      System.out.println("打电话");
   }
   //这里没有锁,不是同步方法,不受锁的影响
   public void hello(){
      System.out.println("hello");
   }
}

结果:


package com.example.juc.test;
import java.util.concurrent.TimeUnit;
/*
 * 5.增加两个静态的同步方法,只有一个对象,先打印 发短信还是打电话
 * 6.两个对象!增加两个静态的同步方法, 先打印 发短信还是打电话
 * */
public class Demo1 {
   public static void main(String[] args) throws InterruptedException {
      //两个对象的Class类模板只有一个,static,锁的是Class
      Phone phone = new Phone();
      Phone phone2 = new Phone();
      //锁的存在
      new Thread(()->{
         phone.sendSms();
      },"A").start();
      TimeUnit.SECONDS.sleep(1);
      new Thread(()->{
         phone2.call();
      },"B").start();
   }
}
//Phone唯一的一个Class对象
class Phone{
   //synchronized 锁的对象是方法的调用者
   //static 静态方法
   //类一加载就有了!锁的是Class
   public static synchronized void sendSms(){
      try {
         TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println("发短信");
   }
   public static synchronized void call(){
      System.out.println("打电话");
   }
}

结果:


package com.example.juc.test;
import java.util.concurrent.TimeUnit;
/*
 * 7.1个静态的同步方法,1个普通的同步方法,1个对象,先打印谁
 * 8.1个静态的同步方法,1个普通的同步方法,2个对象,先打印谁
 * */
public class Demo1 {
   public static void main(String[] args) throws InterruptedException {
      //两个对象的Class类模板只有一个,static,锁的是Class
      Phone phone = new Phone();
      Phone phone2 = new Phone();
      //锁的存在
      new Thread(() -> {
         phone.sendSms();
      }, "A").start();
      TimeUnit.SECONDS.sleep(1);
      new Thread(() -> {
         phone2.call();
      }, "B").start();
   }
}
//Phone唯一的一个Class对象
class Phone {
   //静态的同步方法  锁的是Class类模板
   public static synchronized void sendSms() {
      try {
         TimeUnit.SECONDS.sleep(4);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      System.out.println("发短信");
   }
   //普通的同步方法   锁的调用者
   public synchronized void call() {
      System.out.println("打电话");
   }
}

结果:

持续更新中.............

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

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

相关文章

纯电驱动车辆动力总成的优化与比较研究

摘要&#xff1a; 不同动力总成拓扑结构的对比分析 前言 纯电驱动的电动汽车因为集成有大容量电池组&#xff0c;可以存储取自公共电网的电能&#xff0c;用来驱动车辆的行驶。相比于传统的混合动力汽车&#xff0c;具有更加优越的节能减排效果和潜力。因此&#xff0c;近年来…

cpp文件编译过程 makefile cmake

这里写目录标题 argc argv参数头文件编译过程静态链接&#xff0c;动态链接&#xff0c;静态库&#xff0c;动态库 -shared制作使用动态库libxxx。so冲突 静态库预处理编译汇编链接目录选项-Idir 大写Iinclude<> 与 " "-I -I- 与 -I- -I ld/objdump/objcopy 选…

内网渗透—隧道搭建Ngrok与Frp内网穿透

这里写目录标题 1. 前言1.1. 隧道技术介绍1.2. 代理技术介绍1.2.1. 正向代理1.2.2. 反向代理1.2.3. 透明代理1.2.4. 正向代理与透明代理区别 2. 内网穿透2.1. Ngrok2.1.1. 访问Ngrok2.1.2. 代理设置2.1.2.1. 开通代理2.1.2.2. 配置隧道2.1.2.3. 下载客户端 2.1.3. 配置客户端2.…

P20[6-8]编码器接口测速(软)

与外部中断编码器逻辑不同,此处编码器使用的是定时器方法 1.Encoder编码器部分: #include "stm32f10x.h" // Device header void Encoder_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCm…

吐血整理,自动化测试场景处理(多场景覆盖)+解决方案

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、添加时间等待 …

53.最大子数组和

贪心算法 时间复杂度&#xff1a;代码中只有一个循环&#xff0c;循环次数为数组的长度&#xff0c;因此时间复杂度为 O(n)&#xff0c;其中 n 是数组的长度。空间复杂度&#xff1a;代码中只使用了常数级别的额外空间&#xff0c;因此空间复杂度为 O(1)。 这段代码使用贪心算…

QT C++入门学习(2) QT Creator写一个简单的上位机控制LED

上位机和下位机的概念 上位机&#xff1a;指的是可以直接发送操作指令的计算机或者单片机&#xff0c;一般提供用户操作交互界面并向用户展示反馈数据。 典型设备&#xff1a;电脑、平板、手机、面板、触摸屏 下位机&#xff1a;指的是与机器相连接的计算机或者单片机&#…

Studio 3T 2023.5 (macOS, Linux, Windows) - MongoDB 的专业 GUI、IDE 和 客户端

Studio 3T 2023.5 (macOS, Linux, Windows) - MongoDB 的专业 GUI、IDE 和 客户端 请访问原文链接&#xff1a;https://sysin.org/blog/studio-3t-2023/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Studio 3T&#xff0c;M…

黑客松必备|Bear Necessities Hackathon Office Hours汇总

由Moonbeam和AWS Startups联合主办的Bear Necessities Hackathon黑客松启动仪式已于5月30日举行。本次黑客松将历时约1个月的时间&#xff0c;包含6个挑战&#xff0c;分别由Moonbeam基金会、Chainlink、StellaSwap、SubQuery、Biconomy提供赞助&#xff0c;总奖池超过5万美金。…

面试软件测试时,面试官最想听到的答案是什么?

问题&#xff1a;面试软件测试时&#xff0c;面试官让你测一个软件&#xff0c;比如朋友圈&#xff0c;或者让你测试你的电脑为什么打不开网页&#xff0c;而QQ可以打开之类的&#xff0c;他最想听到的答案是什么&#xff1f; 如上所述&#xff0c;在实际面试中&#xff0c;面试…

懂点测试基础就敢要17k? 面试官:最多8K,多一分都没有...

公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在10-25k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是3年工作经验&#xff0c;但面试…

Java泛型的使用

1.什么是泛型&#xff1f; 所谓泛型&#xff0c;就是允许在定义类、接口时通过 一个标识 表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时&#xff08;例如&#xff0c;继承或实现这个接口&#xff0c;用这个类型声明变量、创建对象时&#…

5.Opencv-图像滤波(均值,方框,高斯,中值,双边滤波)

常见的图像滤波操作有&#xff1a; 均值滤波&#xff08;cv2.blur&#xff09; 方框滤波&#xff08;cv2.boxFilter&#xff09; 高斯滤波&#xff08;cv2.GaussianBlur&#xff09; 中值滤波&#xff08;cv2.medianBlur&#xff09; 双边滤波&#xff08;cv2.bilateralFilter…

电脑小白不要错过这五款小众但强大的软件

电脑上的各类软件有很多&#xff0c;除了那些常见的大众化软件&#xff0c;还有很多不为人知的小众软件&#xff0c;专注于实用功能&#xff0c;简洁干净、功能强悍。 多语言翻译——QTranslate QTranslate是一款实用的多语言翻译工具。它可以在任何应用程序中选中文本&#…

【C++】模版进阶

目录 一、非类型模版参数二、模板的特化1、概念2、函数模版特化3、类模板特化1.全特化2.偏特化3.类模板特化应用示例 三、模版分离编译1、什么是分离编译2、模板的分离编译3、模板的优缺点 一、非类型模版参数 模版参数分为类型模版参数与非类型模版参数 类型模版参数&#x…

一定要看!带你选择适合自己的测试工具

目录 前言&#xff1a; Jmeter实现接口请求JSON断言 Postman接口请求断言 前言&#xff1a; 选择适合的测试工具对于测试人员和测试项目的成功非常重要。不同的测试工具都有其独特的优缺点&#xff0c;而且每个项目的需求也不尽相同。因此&#xff0c;在选择测试工具时&#…

使用vitepress快速搭建个人网站或官方文档网站

使用vitepress快速搭建个人网站或官方文档网站 1. vitepress是什么&#xff1f; 官方首页的介绍&#xff0c; 翻译过来就是&#xff0c;vite和vue组成的强大的静态网站构造器。简单、强大和快速&#xff0c;是你一直想要的SSG(Static Site Generator)框架。 官网地址&#…

python mitmproxy抓包库

一.简介 mitmproxy是一款用Python编写的支持HTTP(S)的中间人代理工具。它可以拦截、查看、修改、重放和保存HTTP/HTTPS流量 &#xff0c;支持命令行界面和图形界面&#xff0c;可用于安全测试、网络调试、API开发和反向工程等场景。mitmproxy具有很高的灵活性和扩展性&#xf…

GUT|IF30+的联合分析文章:宏基因加代谢组

● 代谢组学是基于LC-MS/MS液质联用技术对生物样本中的小分子代谢物进行定性和相对定量分析&#xff1b; ● 宏基因组-代谢组的联合分析可以用来解释差异菌群与差异代谢物的关联性&#xff1b; ● 从而帮助建立微生物-代谢物-表型之间的逻辑关系。 凌恩生物的宏基因组学引入了…

JS:yFiles for HTML Complete 2.5.0 Crack

yFiles for HTML Complete 是市场上最先进、最完整的图表解决方案。我们强大而灵活的 API 提供了广泛的功能——开箱即用。只需选择最符合您需求的那些。 适用于每个用例的布局 从大量预定义的布局中进行选择并配置它们以完美地适应手头的任务。 yFiles 提供业内最广泛的高质量…