Java多线程核心技术二-synchronzied同步方法

news2024/9/29 9:30:53

1 概述

        关键字synchronzied保障了原子性、可见性和有序性。

        非线程安全问题会在多个线程对同一个对象中的同一个实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是读取到的数据其实是被更改过的。而线程安全是指获取的实例变量的值是经过同步处理的,不会出现脏读的现象。本篇将细化线程并发访问的内容,在细节上更多讲解并发时变量值的处理方法。

2 方法内的变量是线程安全的

        非线程安全问题存在时实例变量中,如果是方法内部私有变量,则不存在非线程安全问题,结果是线程安全的。

【示例1.1.1】演示方法内部声明一个变量时,是不存在“非线程安全”问题的。

public class HasSelfPrivateNum {
    public void addI(String username){
        try {
            int num = 0;
            if(username.equals("a")){
                num = 100;
                System.out.println("a set over");
                Thread.sleep(4000);
            }else{
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(username + "num = " +num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

}

   

public class ThreadA extends Thread{
    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        this.numRef = numRef;
    }
    @Override
    public void run(){
        numRef.addI("a");
    }
}

   

public class ThreadB extends Thread{
    private HasSelfPrivateNum refNef ;

    public ThreadB(HasSelfPrivateNum refNef) {
        this.refNef = refNef;
    }
    @Override
    public void run(){
        refNef.addI("b");
    }
}
public class RunDemo201 {
    public static void main(String[] args) {
        HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
        ThreadA a = new ThreadA(hasSelfPrivateNum);
        ThreadB b = new ThreadB(hasSelfPrivateNum);
        a.start();
        b.start();
    }
}

3 实例变量“非线程安全”问题及解决方案

        如果多个线程共同访问一个对象中的实例变量,则有可能出现非线程安全问题。线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。把上面 HasSelfPrivateNum类中addI()方法的局部变量改为全局变量。在执行,num有可能被覆盖。

public class HasSelfPrivateNum {
    private int num = 0;
    public void addI(String username){
        try {

            if(username.equals("a")){
                num = 100;
                System.out.println("a set over");
                Thread.sleep(4000);
            }else{
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(username + "num = " +num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

}

        这个实验是两个线程同时访问一个业务对象中的同一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现非线程安全问题,根据前面的介绍,只需要在方法加上synchronized即可,更改后的代码:

public class HasSelfPrivateNum {
    private int num = 0;
    synchronized public void addI(String username){
        try {

            if(username.equals("a")){
                num = 100;
                System.out.println("a set over");
                Thread.sleep(4000);
            }else{
                num = 200;
                System.out.println("b set over");
            }
            System.out.println(username + "num = " +num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

}

 

4 同步synchronzied在字节码指令中的原理

        在方法上使用synchronzied关键字实现同步的原因是使用了flag标记ACC_SYNCHRONZIED,当调用方法时,调用质量会检查方法的ACC_SYNCHRONZIED访问标志是否设置,如果设置了,执行线程先持有同步锁,然后执行方法,最后在方法完成时释放锁。

【示例1.3.1】测试代码

public class TestSynchronzied {
    synchronized public  static void testMethod(){

    }

    public static void main(String[] args) throws InterruptedException{
        testMethod();
    }
}

使用javap命令把class文件转换成字节码指令,如下:

javap -c -v TestSynchronzied.class

生成这个class文件对应的字节码指令,指令的核心代码如下:

 public com.jay.current.demo213.TestSynchronzied();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/jay/current/demo213/TestSynchronzied;

  public static synchronized void testMethod();
    descriptor: ()V
    flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 6: 0

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: invokestatic  #7                  // Method testMethod:()V
         3: return
      LineNumberTable:
        line 9: 0
        line 10: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  args   [Ljava/lang/String;
    Exceptions:
      throws java.lang.InterruptedException

        在反编译的字节码指令中对public synchronzied void testMethod()方法使用了flag标记ACC_SYNCHRONZIED,说明此方法时同步的。

        如果使用synchronzied代码块,则使用monitorenter和monitorexit指令进行同步处理。测试代码如下:

public class TestSynchronzied_2 {
    public void testMethod(){
        synchronized (this){
            int age = 100;
        }
    }

    public static void main(String[] args) throws InterruptedException{
        TestSynchronzied_2 testSynchronzied2 = new TestSynchronzied_2();
        testSynchronzied2.testMethod();
    }
}

在CMD执行命令:

javap -c -v TestSynchronzied_2.class

生成的字节码指令如下:

 public void testMethod();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: bipush        100
         6: istore_2
         7: aload_1
         8: monitorexit
         9: goto          17
        12: astore_3
        13: aload_1
        14: monitorexit
        15: aload_3
        16: athrow
        17: return
      Exception table:
         from    to  target type
             4     9    12   any
            12    15    12   any
      LineNumberTable:
        line 5: 0
        line 6: 4
        line 7: 7
        line 8: 17
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      18     0  this   Lcom/jay/current/demo213/TestSynchronzied_2;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 12
          locals = [ class com/jay/current/demo213/TestSynchronzied_2, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #7                  // class com/jay/current/demo213/TestSynchronzied_2
         3: dup
         4: invokespecial #9                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #10                 // Method testMethod:()V
        12: return
      LineNumberTable:
        line 11: 0
        line 12: 8
        line 13: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  args   [Ljava/lang/String;
            8       5     1 testSynchronzied2   Lcom/jay/current/demo213/TestSynchronzied_2;
    Exceptions:
      throws java.lang.InterruptedException

        有代码可知,在字节码使用了monitorenter和monitorexit指令进行同步处理。

5 多个对象多个锁

        【示例1.4.1】

public class HasSelfPrivateNum {
    synchronized public void testMethod(){
        try {
            System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

         上面的代码有同步方法testMethod(),说明此方法在正常情况下应该被顺序调用。在创建两个线程类。

public class ThreadA extends Thread{
    private  HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        this.numRef = numRef;
    }
    @Override
    public  void run(){
        numRef.testMethod();
    }
}
public class ThreadB extends Thread{
    private  HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        this.numRef = numRef;
    }
    @Override
    public  void run(){
        numRef.testMethod();
    }
}

        最后创建main函数类

public class Run1 {
    public static void main(String[] args) {
        HasSelfPrivateNum num1 = new HasSelfPrivateNum();
        HasSelfPrivateNum num2 = new HasSelfPrivateNum();
        ThreadA a = new ThreadA(num1);
        a.start();
        ThreadB b = new ThreadB(num2);
        b.start();
    }
}

        运行结果:

        代码分析:首先创建了两个 HasSelfPrivateNum.java类的对象,即产生了两把锁。两个线程分别访问同一个类的两个不同示例的相同名称的同步方法(testMethod()),控制台输出了两个begin和end,且begin和end不是成对的输出,呈现了两个线程交叉输出的效果,说明两个线程以异步方式同时运行。

        本示例创建了两个业务对象,在系统中产生了两把锁,线程和业务对象属于一对一的关系,每个线程执行自己所属业务对象中的方法,不存在锁的争抢关系,所以运行结果是异步的。另外,在这种情况下synchronzied可以不需要,因为不会出现非线程安全的问题。

        只有多个线程执行统一个业务对象中的同步方法时,线程和业务对象属于多对一的关系,为了避免出现非线程安全问题,所以使用了synchronzied。

        总结:多个线程对共享的资源有写操作,则必须同步,如果只是读操作,则不需要同步。

6 synchronzied方法将对象作为锁

        为了证明上面说的将对象作为锁,开发以下代码:

public class MyObject {
    public  void methodA(){
        try{
            System.out.println("开始执行 methodA方法,线程名为:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadADemo215 extends Thread{
    private MyObject myObject;

    public ThreadADemo215(MyObject myObject) {
        this.myObject = myObject;
    }
    @Override
    public void  run(){
        myObject.methodA();
    }
}
public class ThreadBDemo215 extends Thread{
    private MyObject myObject;

    public ThreadBDemo215(MyObject myObject) {
        this.myObject = myObject;
    }
    @Override
    public void  run(){
        myObject.methodA();
    }
}
public class Run1 {
    public static void main(String[] args) {
        MyObject object = new MyObject();
        ThreadADemo215 a = new ThreadADemo215(object);
        a.setName("A");
        ThreadBDemo215 b = new ThreadBDemo215(object);
        b.setName("B");
        a.start();
        b.start();
    }
}

运行结果

        两个线程一同进入methodA方法,因为该方法没有同步。更改MyObject类,加上synchronzied关键字。

public class MyObject {
    synchronized public  void methodA(){
        try{
            System.out.println("开始执行 methodA方法,线程名为:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

 运行结果:

        通过上面的示例得到结论,调用关键字synchronzied声明的方法一定是排队运行。另外,需要记住“共享”这两个字,只有共享资源的写操作才需要同步,如果不是共享资源,是没有同步的必要的。 那其他方法被调用时会有什么效果呢?如何查看将对象作为锁的效果呢?重新修改MyObject.java类,新增一个没用synchronzied修改的方法methodB()

public class MyObject {
    synchronized public  void methodA(){
        try{
            System.out.println("开始执行 methodA方法,线程名为:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void methodB(){
        try{
            System.out.println("开始执行 methodB方法,线程名为:"+Thread.currentThread().getName());
            Thread.sleep(3000);
            System.out.println("end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

同时修改 ThreadBDemo215.java,run方法调用methodB()。运行结果:

        通过这个示例得知,虽然线程A先持有了object对象的锁,但线程B完全可以异步调用非synchronzied类型的方法。

        在把MyObject.java中的methodB方法加上synchronzied关键字,执行结果如下:

        两个线程访问同一个对象的两个同步方法。结论如下:

        1、A现成先持有object对象的锁,B现成可以以异步的方式调用object对象中的非synchronzied类型的方法。       

        2、A现成先持有object对象的锁,B现成如果在这时调用object对象中的synchronzied类型的方法,则需要等待A线程释放锁。

        3、在方法声明处添加sync并不是锁方法,而是锁当前类的对象。

        4、在java中,只有将对象作为锁,并没有锁方法这种说法。

        5、在Java中,锁就是对象,对象可以映射成锁,哪个线程拿到这把锁,哪个线程就可以执行这个对象中的synchronzied同步方法。

        6、如果在X对象中使用了synchronzied关键词声明非静态方法,则X对象就被当成锁。

7 脏读与解决

        在多个线程调用同一个方法时,为了避免数据出现交叉的情况,使用synchronzied关键字进行同步。虽然在赋值时进行了同步,但是在取值时有可能出现脏读。发生脏读的原因是在读取实例变量时,此值已经被其他线程更改过了。

【示例7.1】 

public class PublicVar {
    public String username = "A";
    public String password = "B";
    synchronized public void setValue(String username,String password){
        try{
            this.username = username;
            Thread.sleep(2000);
            this.password = password;
            System.out.println("执行setValue()方法,线程名是: "+Thread.currentThread().getName() + " username = " + username + ";password = " + password);
            
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
    public void getValue(){
        System.out.println("getValue()方法,线程名是: "+Thread.currentThread().getName() + " username = " + username + ";password = " + password);
    }
}
public class ThreadADemo216 extends Thread{
    private PublicVar publicVar;

    public ThreadADemo216(PublicVar publicVar) {
        this.publicVar = publicVar;
    }
    @Override
    public void run(){
        publicVar.setValue("B","BB");
    }
}
public class Test1 {
    public static void main(String[] args) {
        try {
            PublicVar publicVar = new PublicVar();
            ThreadADemo216 a = new ThreadADemo216(publicVar);
            a.start();
            Thread.sleep(2000);
            publicVar.getValue();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

运行结果:

出现脏读是因为getValue方法并不是同步的,所以可以在任意时候进行调用,解决办法是加上synchronzied。

8 synchronzied锁重入

        关键词synchronzied拥有重入锁的功能,即在使用synchronzied时,当一个线程得到一个对象锁后,再次请求此对象锁时可以再次得到该对象的锁。这也证明在一个synchronzied方法/块的内部调用本类的其他synchronzied方法/this块时,是永远可以得到锁的。 

public class Service {
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized private void service2() {
        System.out.println("service2");
        service3();
    }

    synchronized private void service3() {
        System.out.println("service3");
    }
}
public class MyThread extends Thread{
    
    @Override
    public void run(){
        Service service = new Service();
        service.service1();
    }
}
public class Run1 {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}

        可重入锁是指自己可以再次获取自己的内部锁。例如,有1个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象锁时还可以获取。如果是不可重入锁,方法service2和service3都不会被调用。

9 出现异常,锁自动释放

        当一个线程执行的代码出现异常时,其锁持有的锁会自动释放。

        【示例】

public class Service {
    synchronized public void testMethod(){
        if(Thread.currentThread().getName().equals("a")){
            System.out.println("线程名="+Thread.currentThread().getName()
            +"开始执行时间 = " + System.currentTimeMillis());
            int i = 1;
            while(i == 1){
                if(("" + Math.random()).substring(0,8).equals("0.123456")){
                    System.out.println("线程名="+Thread.currentThread().getName()
                            +"执行异常时间 = " + System.currentTimeMillis());
                    Integer.parseInt("a");
                }
            }
        }else{
            System.out.println("线程B运行时间 = " +System.currentTimeMillis());
        }
    }
    
}

        创建两个自定义线程。

public class ThreadA extends Thread{
    private Service service;

    public ThreadA(Service service) {
        this.service = service;
    }
    @Override
    public void run(){
        service.testMethod();
    }
}
public class ThreadB extends Thread{
    private Service service;

    public ThreadB(Service service) {
        this.service = service;
    }

    @Override
    public void run(){
        service.testMethod();
    }

}
public class Run1 {
    public static void main(String[] args) {
        try {
            Service service = new Service();
            ThreadA a = new ThreadA(service);
            a.setName("a");
            a.start();
            Thread.sleep(500);
            ThreadB b = new ThreadB(service);
            b.setName("b");
            b.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }


    }
}

        线程a出现异常并释放锁,线程b进入方法正常输出,说明出现异常时,锁被自动释放了。

        【注意】Thread.java中的suspend()和sleep()方法被调用后不会释放所。 

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

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

相关文章

坚鹏:中国人寿福建省公司当下宏观经济形势分析与二十大精神解读

中国人寿保险&#xff08;集团&#xff09;公司属国家大型金融保险企业&#xff0c;2016年中国人寿入主广发银行&#xff0c;开启保险、投资、银行三大板块协同发展新格局。2022年&#xff0c;集团公司合并营业收入站稳万亿平台&#xff1b;合并总资产突破6万亿元大关。中国人寿…

java学习part18抽象类

Java抽象类 详解-CSDN博客 111-面向对象(高级)-抽象类与抽象方法的使用_哔哩哔哩_bilibili 1.概念 2.抽象类 抽象类不能实例化&#xff0c;可以有属性&#xff0c;也可以有方法。 方法可以实现或者只声明不实现&#xff0c;要加一个abstract abstract class A{//定义一个抽…

操作系统 选择题 期末试题 考研真题 + 参考答案

1.&#xff08;考研真题&#xff0c;单项选择题&#xff09;单道批处理系统的主要缺点是&#xff08; &#xff09;。 A. CPU利用率不高 B.失去了交互性 C.不具备并行性 D.以上都不是 【参考答案】A 【解析】单道批处理系统的内存中只有一道程序&#xff0c;当该程序…

智能优化算法应用:基于郊狼算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于郊狼算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于郊狼算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.郊狼算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

C 语言-循环嵌套-函数

C 语言 - 循环嵌套、函数 1. 循环嵌套 1.1 作用 循环 套 循环。 1.2 使用 需求1&#xff1a; 打印以下图形&#xff1a; * * * * * * * * * * * * * * * *代码&#xff1a; 1、使用循环打印 #include <stdio.h> int main(int argc, char const *argv[]) {for (int i…

zabbix 6.0 原理与部署

一、zabbix简介&#xff1a; zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。 zabbix 能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 zabbi…

Mysql中的引擎介绍(InnoDB,MyISAM,Memory)

MySQL引擎就是指表的类型以及表在计算机上的存储方式。 MySQL数据库及其分支版本主要的存储引擎有三种&#xff0c;分别是 InnoDB、MyISAM、 Memory&#xff0c;还有一些其他的&#xff0c;CSV、Blackhole等&#xff0c;比较少见&#xff0c;可以使用SHOW ENGINES语句来查看。结…

计算机组成原理——小啃一下

CPU和主存储器结构 CPU&#xff1a; 运算器 ACC&#xff08;累加器&#xff09;ALU&#xff08;算数逻辑单元&#xff09;MQ&#xff08;乘商寄存器&#xff09;X&#xff08;操作数寄存器&#xff09; 控制器 CU&#xff08;控制单元&#xff09;IR&#xff08;指令寄存器&a…

基础课12——深度学习

深度学习技术是机器学习领域中的一个新的研究方向&#xff0c;它被引入机器学习使其更接近于最初的目标——人工智能。深度学习的最终目标是让机器能够像人一样具有分析学习能力&#xff0c;能够识别文字、图像和声音等数据。 深度学习的核心思想是通过学习样本数据的内在规律…

java第二十章总结多线程

20.2创建线程 20.2.1继承Thread类 Thread类是Java.lang包中的一个类&#xff0c;从这个类中实例化的对象代表线程&#xff0c;程序员启动一个新线程需要建议Thread实例。 public class ThreadTest extedns Thread{} run方法格式&#xff1a; public void run(){} 20.1让线程…

纯干货篇,用Stable diffusion制作广告的保姆级教程

今天就想给大家演示一般如何使用Stable diffusion为你的产品添加场景&#xff0c;秒变广告大片。 掌握这个技能&#xff0c;你可以随意将产品添加不同的场景&#xff0c;节约复杂的拍摄布景和拍摄成本。 话不多说&#xff0c;接下来是详细讲解演示流程 首先选出一张你的产品图…

SAP_ABAP_编程基础_基本数据类型(预定义数据类型)介绍 , 它有10种

SAP ABAP 顾问&#xff08;开发工程师&#xff09;能力模型_Terry谈企业数字化的博客-CSDN博客文章浏览阅读441次。目标&#xff1a;基于对SAP abap 顾问能力模型的梳理&#xff0c;给一年左右经验的abaper 快速成长为三年经验提供超级燃料&#xff01;https://blog.csdn.net/j…

完美的输出打印 SQL 及执行时长[MyBatis-Plus系列]

导读 Hi,大家好,我是悟纤。过着爱谁谁的生活,活出不设限的人生。 在我们日常开发工作当中,避免不了查看当前程序所执行的SQL语句,以及了解它的执行时间,方便分析是否出现了慢SQL问题。 MyBatis-Plus提供了两种SQL分析打印的方式,用于输出每条SQL语句及其执行时间,针…

Redis应用的16个场景

常见的16种应用场景: 缓存、数据共享分布式、分布式锁、全局 ID、计数器、限流、位统计、购物车、用户消息时间线 timeline、消息队列、抽奖、点赞、签到、打卡、商品标签、商品筛选、用户关注、推荐模型、排行榜. 1、缓存 String类型 例如&#xff1a;热点数据缓存&#x…

【解决方案】多用户多回路宿舍用电管理解决方案

01 引言 近几年来&#xff0c;因违规使用大功率恶性负载电器导致宿舍失火的安全事故在各大高校时有发生&#xff0c;给学生和学校都带来了巨大的损失。北京大学、哈尔滨工业大学、上海商学院以及俄罗斯人民友谊大学等高校学生公寓发生的火灾给高校学生公寓的安全用电敲响了警钟…

视频封面:视频图片提取技巧,从指定时长中捕捉需求的图片

在当今的数字时代&#xff0c;视频已成为日常生活中不可或缺的一部分。无论是社交媒体、博客&#xff0c;视频都发挥着重要的作用。而一个吸引的视频封面往往能吸引更多的观众点击观看&#xff0c;选择清晰度高、色彩鲜艳且能吸引人的图片。同时&#xff0c;确保图片与视频内容…

【FMC139】青翼科技基于VITA57.1标准的4路500MSPS/1GSPS/1.25GSPS采样率14位AD采集FMC子卡模块

板卡概述 FMC139是一款基于VITA57.1标准规范的JESD204B接口FMC子卡模块&#xff0c;该模块可以实现4路14-bit、500MSPS/1GSPS ADC采集功能。该板卡ADC器件采用ADI公司的AD9680芯片,全功率-3dB模拟输入带宽可达2GHz。该ADC与FPGA的主机接口通过8通道的高速串行GTX收发器进行互联…

捷达EA113汽油机四缸汽车曲柄连杆机构毕业设计

wx供重浩&#xff1a;创享日记 对话框发送&#xff1a;捷达 获取完整论文报告工程源文件 本文以捷达EA113汽油机的相关参数作为参考&#xff0c;对四缸汽油机的曲柄连杆机构的主要零部件进行了结构设计计算&#xff0c;并对曲柄连杆机构进行了有关运动学和动力学的理论分析与计…

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

ESP32-Web-Server编程-JS 基础 1 概述 前述分别在 HTML 基础 和 CSS 基础 中介绍了 HTML、CSS 的基本内容。HTML 定义了网页中包含哪些对象&#xff0c;CSS 定义了对象的显示样式。JavaScript(LiveScript)是一种运行于客户端的解释性脚本语言&#xff0c;使 HTML 页面更具动态…

Java第二十章多线程

线程简介 在 Java 中&#xff0c;并发机制非常重要。在以往的程序设计中&#xff0c;我们都是一个任务完成后再进行下一个任务&#xff0c;这样下一个任务的开始必须等待前一个任务的结束。Java 语言提供了并发机制&#xff0c;程序员可以在程序中执行多个线程&#xff0c;每一…