Java之戳中痛点之 synchronized 深度解析

JUC核心解析系列(二)——显式锁深度解析 作为 Java 并发编程的终极武器,Lock 接口在高手手中能爆发出惊人的性能!本文将深度剖析其核心原理和实战技巧,助你彻底掌握这把高性能锁! 阅读详情

概览:

  • 简介:作用、地位、不控制并发的影响

  • 用法:对象锁和类锁

  • 多线程访问同步方法的7种情况

  • 性质:可重入、不可中断

  • 原理:加解锁原理、可重入原理、可见性原理

  • 缺陷:效率低、不够灵活、无法预判是否成功获取到锁

  • 如何选择Lock或Synchronized

  • 如何提高性能、JVM如何决定哪个线程获取锁

  • 总结

后续会有代码演示,测试环境 JDK8、IDEA

一、简介

1、作用

能够保证在同一时刻最多只有一个线程执行该代码,以保证并发安全的效果。

2、地位

  • Synchronized是Java关键字,Java原生支持

  • 最基本的互斥同步手段

  • 并发编程的元老级别

3、不控制并发的影响

测试:两个线程同时a++,猜一下结果

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 不使用synchronized,两个线程同时a++  
 *  
 * @author JSON  
 */  
public class SynchronizedTest1 implements Runnable{  
    static SynchronizedTest1 st = new SynchronizedTest1();  
  
    static int a = 0;  
  
    /**  
     * 不使用synchronized,两个线程同时a++  
     */  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(st);  
        Thread t2 = new Thread(st);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println(a);  
    }  
  
    @Override  
    public void run(){  
        for(int i=0; i<10000; i++){  
            a++;  
        }  
    }  
}  

预期是20000,但多次执行的结果都小于20000

10108  
11526  
10736  
...  

二、用法:对象锁和类锁

1、对象锁

  • 代码块形式:手动指定锁对象

  • 方法锁形式:synchronized修饰方法,锁对象默认为this

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 对象锁实例: 代码块形式  
 *  
 * @author JSON  
 */  
public class SynchronizedTest2 implements Runnable{  
    static SynchronizedTest2 st = new SynchronizedTest2();  
  
    public static void main(String[] args) {  
        Thread t1 = new Thread(st);  
        Thread t2 = new Thread(st);  
        t1.start();  
        t2.start();  
        while(t1.isAlive() || t2.isAlive()){  
  
        }  
        System.out.println("run over");  
  
    }  
  
    @Override  
    public void run(){  
        synchronized (this){  
            System.out.println("开始执行:" + Thread.currentThread().getName());  
            try {  
                // 模拟执行内容  
                Thread.sleep(3000);  
            }catch (Exception e){  
                e.printStackTrace();  
            }  
            System.out.println("执行结束:" + Thread.currentThread().getName());  
        }  
    }  
}  

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 对象锁实例:synchronized方法  
 * @author JSON  
 */  
public class SynchronizedTest3 implements Runnable{  
    static SynchronizedTest3 st = new SynchronizedTest3();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(st);  
        Thread t2 = new Thread(st);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public synchronized void method(){  
        System.out.println("开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("执行结束:" + Thread.currentThread().getName());  
    }  
}  

结果:

开始执行:Thread-0  
执行结束:Thread-0  
开始执行:Thread-1  
执行结束:Thread-1  
run over  

2、类锁

**概念:**Java类可能有多个对象,但只有一个Class对象

**本质:**所谓的类锁,不过是Class对象的锁而已

**用法和效果:**类锁只能在同一时刻被一个对象拥有

形式1:synchronized加载static方法上

形式2:synchronized(*.class)代码块

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 类锁:synchronized加载static方法上  
 *  
 * @author JSON  
 */  
public class SynchronizedTest4 implements Runnable{  
  
    static SynchronizedTest4 st1 = new SynchronizedTest4();  
    static SynchronizedTest4 st2 = new SynchronizedTest4();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(st1);  
        Thread t2 = new Thread(st2);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public static synchronized void method(){  
        System.out.println("开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("执行结束:" + Thread.currentThread().getName());  
    }  
}  

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 类锁:synchronized(*.class)代码块  
 *  
 * @author JSON  
 */  
public class SynchronizedTest5 implements Runnable{  
    static SynchronizedTest4 st1 = new SynchronizedTest4();  
    static SynchronizedTest4 st2 = new SynchronizedTest4();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(st1);  
        Thread t2 = new Thread(st2);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public void method(){  
        synchronized(SynchronizedTest5.class){  
            System.out.println("开始执行:" + Thread.currentThread().getName());  
            try {  
                // 模拟执行内容  
                Thread.sleep(3000);  
            }catch (Exception e){  
                e.printStackTrace();  
            }  
            System.out.println("执行结束:" + Thread.currentThread().getName());  
        }  
    }  
}  

结果:

开始执行:Thread-0  
执行结束:Thread-0  
开始执行:Thread-1  
执行结束:Thread-1  
run over  


三、多线程访问同步方法的7种情况


  1. 两个线程同时访问一个对象的相同的synchronized方法

  2. 两个线程同时访问两个对象的相同的synchronized方法

  3. 两个线程同时访问两个对象的相同的static的synchronized方法

  4. 两个线程同时访问同一对象的synchronized方法与非synchronized方法

  5. 两个线程访问同一对象的不同的synchronized方法

  6. 两个线程同时访问同一对象的static的synchronized方法与非static的synchronized方法

  7. 方法抛出异常后,会释放锁吗

仔细看下面示例代码结果输出的结果,注意输出时间间隔,来预测结论

场景1:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程同时访问一个对象的相同的synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene1 implements Runnable{  
    static SynchronizedScene1 ss = new SynchronizedScene1();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss);  
        Thread t2 = new Thread(ss);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public synchronized void method(){  
        System.out.println("开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景2:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程同时访问两个对象的相同的synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene2 implements Runnable{  
    static SynchronizedScene2 ss1 = new SynchronizedScene2();  
    static SynchronizedScene2 ss2 = new SynchronizedScene2();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss2);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public synchronized void method(){  
        System.out.println("开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景3:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程同时访问两个对象的相同的static的synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene3 implements Runnable{  
    static SynchronizedScene3 ss1 = new SynchronizedScene3();  
    static SynchronizedScene3 ss2 = new SynchronizedScene3();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss2);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method();  
    }  
  
    public synchronized static void method(){  
        System.out.println("开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景4:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程同时访问同一对象的synchronized方法与非synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene4 implements Runnable{  
    static SynchronizedScene4 ss1 = new SynchronizedScene4();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss1);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        // 模拟两个线程同时访问 synchronized方法与非synchronized方法  
        if(Thread.currentThread().getName().equals("Thread-0")){  
            method1();  
        }else{  
            method2();  
        }  
    }  
  
    public void method1(){  
        System.out.println("method1开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method1执行结束:" + Thread.currentThread().getName());  
    }  
  
    public synchronized void method2(){  
        System.out.println("method2开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method2执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景5:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程访问同一对象的不同的synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene5 implements Runnable{  
    static SynchronizedScene5 ss1 = new SynchronizedScene5();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss1);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        // 模拟两个线程同时访问不同的synchronized方法  
        if(Thread.currentThread().getName().equals("Thread-0")){  
            method1();  
        }else{  
            method2();  
        }  
    }  
  
    public synchronized void method1(){  
        System.out.println("method1开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method1执行结束:" + Thread.currentThread().getName());  
    }  
  
    public synchronized void method2(){  
        System.out.println("method2开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method2执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景6:

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 两个线程同时访问同一对象的static的synchronized方法与非static的synchronized方法  
 *  
 * @author JSON  
 */  
public class SynchronizedScene6 implements Runnable{  
    static SynchronizedScene6 ss1 = new SynchronizedScene6();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss1);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        // 模拟两个线程同时访问static的synchronized方法与非static的synchronized方法  
        if(Thread.currentThread().getName().equals("Thread-0")){  
            method1();  
        }else{  
            method2();  
        }  
    }  
  
    public static synchronized void method1(){  
        System.out.println("method1开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method1执行结束:" + Thread.currentThread().getName());  
    }  
  
    public synchronized void method2(){  
        System.out.println("method2开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        System.out.println("method2执行结束:" + Thread.currentThread().getName());  
    }  
}  

场景7:

在公众号顶级架构师回复“架构整洁”,获取惊喜礼包。

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 方法抛出异常后,会释放锁吗  
 *  
 * @author JSON  
 */  
public class SynchronizedScene7 implements Runnable{  
    static SynchronizedScene7 ss1 = new SynchronizedScene7();  
  
    public static void main(String[] args) throws Exception{  
        Thread t1 = new Thread(ss1);  
        Thread t2 = new Thread(ss1);  
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
        System.out.println("run over");  
    }  
  
    @Override  
    public void run(){  
        method1();  
    }  
  
    public synchronized void method1(){  
        System.out.println("method1开始执行:" + Thread.currentThread().getName());  
        try {  
            // 模拟执行内容  
            Thread.sleep(3000);  
        }catch (Exception e){  
            e.printStackTrace();  
        }  
        // 模拟异常  
        throw new RuntimeException();  
        //System.out.println("method1执行结束:" + Thread.currentThread().getName());  
    }  
}  


总结:

1、两个线程同时访问一个对象的相同的synchronized方法

同一实例拥有同一把锁,其他线程必然等待,顺序执行

2、两个线程同时访问两个对象的相同的synchronized方法

不同的实例拥有的锁是不同的,所以不影响,并行执行

3、两个线程同时访问两个对象的相同的static的synchronized方法

静态同步方法,是类锁,所有实例是同一把锁,其他线程必然等待,顺序执行

4、两个线程同时访问同一对象的synchronized方法与非synchronized方法

非synchronized方法不受影响,并行执行

5、两个线程访问同一对象的不同的synchronized方法

同一实例拥有同一把锁,所以顺序执行(说明:锁的是this对象==同一把锁)

6、两个线程同时访问同一对象的static的synchronized方法与非static的synchronized方法

static同步方法是类锁,非static是对象锁,原理上是不同的锁,所以不受影响,并行执行

7、方法抛出异常后,会释放锁吗

会自动释放锁,这里区别Lock,Lock需要显示的释放锁

3个核心思想:

  • 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应1、5的情景)

  • 每个实例都对应有自己的一把锁,不同的实例之间互不影响;例外:锁对象是*.class以及synchronized被static修饰的时候,所有对象共用同一把锁(对应2、3、4、6情景)

  • 无论是方法正常执行完毕还是方法抛出异常,都会释放锁(对应7情景)

补充:

问题:目前进入到被synchronized修饰的方法,这个方法里边调用了非synchronized方法,是线程安全的吗?在公众号顶级架构师回复“架构整洁”,获取惊喜礼包。

package cn.jsonshare.java.base.synchronizedtest;  
  
/**  
 * 目前进入到被synchronized修饰的方法,这个方法里边调用了非synchronized方法,是线程安全的吗?  
 *  
 * @author JSON  
 */  
public class SynchronizedScene8 {  
    public static void main(String[] args) {  
        new Thread(() -> {  
            method1();  
        }).start();  
  
        new Thread(() -> {  
            method1();  
        }).start();  
    }  
  
    public static synchronized void method1() {  
        method2();  
    }  
  
    private static void method2() {  
        System.out.println(Thread.currentThread().getName() + "进入非Synchronized方法");  
        try {  
            Thread.sleep(3000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
  
        System.out.println(Thread.currentThread().getName() + "结束非Synchronized方法");  
    }  
}  

结论:这样是线程安全的

四、性质

1、可重入

指的是同一线程的外层函数获取锁之后,内层函数可以直接再次获取该锁

**Java典型的可重入锁:**synchronized、ReentrantLock

**好处:**避免死锁,提升封装性

**粒度:**线程而非调用

  • 情况1:证明同一方法是可重入的

  • 情况2:证明可重入不要求是同一方法

  • 情况3:证明可重入不要求是同一类中的

2、不可中断

一旦这个锁被别的线程获取了,如果我现在想获得,我只能选择等待或者阻塞,直到别的线程释放这个锁,如果别的线程永远不释放锁,那么我只能永远的等待下去。

相比之下,Lock类可以拥有中断的能力,第一点:如果我觉得我等待的时间太长了,有权中断现在已经获取到锁的线程执行;第二点:如果我觉得我等待的时间太长了不想再等了,也可以退出。

五、原理

1、加解锁原理(现象、时机、深入JVM看字节码)

**现象:**每一个类的实例对应一把锁,每一个synchronized方法都必须首先获得调用该方法的类的实例的锁,方能执行,否则就会阻塞,方法执行完成或者抛出异常,锁被释放,被阻塞线程才能获取到该锁,执行。

**获取和释放锁的时机:**内置锁或监视器锁

package cn.jsonshare.java.base.synchronizedtest;  
  
import java.util.concurrent.locks.Lock;  
import java.util.concurrent.locks.ReentrantLock;  
  
/**  
 * method1 等价于 method2  
 *  
 * @author JSON  
 * @date 2019-08-29  
 */  
public class SynchronizedToLock1 {  
    Lock lock = new ReentrantLock();  
  
    public synchronized void method1(){  
        System.out.println("执行method1");  
    }  
  
    public void method2(){  
        lock.lock();  
        try {  
            System.out.println("执行method2");  
        }catch (Exception e){  
            e.printStackTrace();  
        }finally {  
            lock.unlock();  
        }  
    }  
  
    public static void main(String[] args) {  
        SynchronizedToLock1 sl = new SynchronizedToLock1();  
  
        // method1 等价于 method2  
        sl.method1();  
        sl.method2();  
    }  
}  

深入JVM看字节码:

...  
monitorenter指令  
...  
monitorexit指令  
...  

2、可重入原理(加锁次数计数器)

JVM负责跟踪对象被加锁的次数

线程第一次给对象加锁的时候,计数变为1,每当这个相同的线程在此对象上再次获得锁时,计数会递增

每当任务离开时,计数递减,当计数为0的时候,锁被完全释放

3、可见性原理(内存模型)

Java内存模型

线程A向线程B发送数据的过程(JMM控制)

synchronized关键字实现可见性:

被synchronized修饰,那么执行完成后,对对象所做的任何修改都要在释放锁之前,都要从线程内存写入到主内存,所以主内存中的数据是最新的。

六、缺陷

1、效率低

1)、锁的释放情况少(线程执行完成或者异常情况释放)

2)、试图获得锁时不能设定超时(只能等待)

3)、不能中断一个正在试图获得锁的线程(不能中断)

2、不够灵活

加锁和释放的时机比较单一,每个锁仅有单一的条件(某个对象),可能是不够的

比如:读写锁更灵活

3、无法预判是否成功获取到锁

七、常见问题

1、synchronized关键字注意点:

  • 锁对象不能为空

  • 作用域不宜过大

  • 避免死锁

2、如何选择Lock和synchronized关键字?

总结建议(优先避免出错的原则):

  • 如果可以的话,尽量优先使用java.util.concurrent各种类(不需要考虑同步工作,不容易出错)

  • 优先使用synchronized,这样可以减少编写代码的量,从而可以减少出错率

  • 若用到Lock或Condition独有的特性,才使用Lock或Condition

八、总结

一句话总结synchronized:

JVM会自动通过使用monitor来加锁和解锁,保证了同一时刻只有一个线程可以执行指定的代码,从而保证线程安全,同时具有可重入和不可中断的特性。

我的库存,需要的小伙伴请扫下方csdn官方二维码备注暗号“2”免费领取

在这里插入图片描述

win10锁屏或登录时会自动弹出触摸按键的问题解决办法 本篇文章主要讲解win10锁屏或登录时会自动弹出触摸按键的问题解决方式。日期:2023年5月17日作者:任聪聪。 阅读详情

相关推荐

忆阻器(二)——忆阻模拟器

除了真实的器件,我们也可以用运放和乘法器来搭忆阻模拟器。这节将介绍数学模型、matlab仿真及multisim仿真。 一、数学模型 忆阻分为磁控和荷控两种,这里介绍三次非线性磁控忆阻模型: {I=W(φ)VW(φ)=a+3bφ2dφdt=V \begin{cases} I=W(\varphi)V \\ W(\varphi)=a+3b \varphi ^2\\ \frac{d \varphi }{dt}=V \end{cases} ⎩⎪⎨⎪⎧​I=W(φ)VW(φ)=a+3bφ2dtdφ​=V​ 这里的WWW

http://www.icfgblog.com/ 1万+

Java之戳中痛点 - (8)synchronized深度解析

概览: 简介:作用、地位、不控制并发的影响 用法:对象锁和类锁 多线程访问同步方法的7种情况 性质:可重入、不可中断 原理:加解锁原理、可重入原理、可见性原理 缺陷:效率低、不够灵活、无法预判是否成功获取到锁 如何选择Lock或Synchronized 如何提高性能、JVM如何决定哪个线程获取锁 总结 后续会有代码演示,测试环境 JDK8、IDEA 一、简介 1、作用 能够保证在==...

Json_wangqiang的博客 466

从车机到广告屏:CS5523芯片在5大显示场景的应用方案全解析

本文全面解析了CS5523芯片在车载显示、数字广告机、医疗设备和工业HMI等五大场景的应用方案。作为高性能MIPI转eDP芯片,CS5523凭借出色的兼容性和低功耗特性,成为国产替代方案的优选。文章详细介绍了芯片架构、核心技术及实际应用中的设计要点,为工程师提供全面的技术参考。

julia4scientist的博客 904

探究synchronized底层原理(基于JAVA8源码分析)

JVM支持方法级和方法内部一段指令序列的同步,都用同步锁(monitor)实现 synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入临界区,同时它还可以保证共享变量的内存可见性 Java中每一个对象都可以作为锁,这是synchronized实现同步的基础 1. 普通同步方法...

weixin_34097242的博客 158

电压比较器---滞回比较器(施密特触发器)

当输入电压从小电压开始增加,当其大于VT+时,电压比较器输出低电平,此时阈值电压变成下门限电压(VT-);当输入电压从大电压开始减小,当其小于VT-时,电压比较器输出高电平,此时阈值电压变成上门限电压(VT+);只有当第一次超过上门限电压或者下门限电压值,电压比较器的输出才会翻转变化;Uo为比较输出电压,有两种电压状态VoH(高电平)和VoL(低电平);当Uo输出为高时,电压比较器的阈值电压是上门限电压(VT+);当Uo输出为低时,电压比较器的阈值电压是下门限电压(VT-);Vref为比较参考电压;

weixin_69076249的博客 1万+

ACC自适应巡航系统

ACC自适应巡航系统可在高速驾驶情况下,自动控制纵向跟车距离,减轻驾驶员的工作量。该功能主要依靠安装在车辆前挡风玻璃上的摄像头和布置在格栅附近的雷达进行融合实现。

未知艺术家的博客 207

Java多线程 -- 深入理解JMM(Java内存模型) --(一)基础

Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。如果编写多线程程序的Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

NowOrNever 5789

【图像增强(Image Enhancement )】专栏的相关说明,包含适配人群、专栏简介、专栏亮点、阅读方法、定价理由、品质承诺、关于更新、关于答疑、文章目录、与我联系等(持续更新中...)

专栏内涉及的算法都是基于深度学习的图像增强算法;收录的文章基本上是基于Pytorch框架实现的,部分基于Tensorflow;预计完成100篇论文,并持续更新;专栏内文章主要为两部分:【论文精读】与【论文复现】论文精读:读懂论文,总结提炼,聚焦核心内容,不只是全文翻译论文复现:跑通流程,源码解析,提升代码能力,得到去噪结果以及指标计算综合而言,从大到小拆解模型结构,从小到大实现模型搭建。实现论文与源码的统一,深入理解论文行文逻辑与代码实现逻辑,融汇贯通二者思想,并学以致用。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 4054

深度分析:Java并发系列之volatile,看完你就理解了!

讲到Java并发,多线程编程,一定避免不了对关键字volatile的了解,那么如何来认识volatile,从哪些方面来了解它会比较合适呢? 个人认为,既然是多线程编程,那我们在平常的学习中,工作中,大部分都接触到的就是线程安全的概念。 而线程安全就会涉及到共享变量的概念,所以首先,我们得弄清楚共享变量是什么,且处理器和内存间的数据交互机制是如何导致共享变量变得不安全。 共享变量 能够在多个线程间被多个线程都访问到的变量,我们称之为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存

欢迎您访问我的公众号【前程有光】 325

手把手教你学simulink(69.3)--毫米波5G/6G 系统场景实例设计并实现基于Simulink的毫米波多用户MIMO通信系统模型

毫米波频段(30 GHz到300 GHz)因其高带宽和高速率特性,成为5G和未来6G通信系统的理想选择。然而,毫米波信号的传播特性较差,容易受到大气吸收、雨衰、建筑物遮挡等因素的影响。为了克服这些挑战并提高通信容量,多用户MIMO(MU-MIMO, Multi-User Multiple-Input Multiple-Output)技术被广泛应用。多用户MIMO技术通过在基站和多个用户之间同时传输多个数据流,充分利用空间资源,从而显著提升系统容量和频谱效率。

MHD0815的博客 495

手把手教你学Simulink--进阶电机控制场景实例:基于Simulink的直流电机双闭环调速系统抗负载扰动仿真

摘要:本文基于Simulink平台构建直流电机双闭环调速系统,重点研究其在工业传送带场景下的抗负载扰动性能。系统采用转速外环(稳速)和电流内环(限流)的嵌套结构,通过工程法整定PI参数,实现了转速跌落<10%、恢复时间<0.5s的优异抗扰性能。仿真结果表明,相比单闭环系统,双闭环控制使转速跌落率降低47%,恢复时间缩短75%。文中详细介绍了数学模型建立、Simulink模块实现及参数优化方法,并提供了完整代码和模型文件,为电机控制系统设计提供实用参考。

MHD0815的博客 180

JQuery、JSON、AJAX、XML、IO流、多线程、反射核心知识点详解

JQuery、JSON、AJAX、XML、IO流、多线程、反射随笔,查漏补缺用

a2285786446的博客 1473

不加行也能填满:用 ABAP BDC 驯服 Table Control 滚动录制缺陷,批量维护供应商银行账户

摘要 本文探讨了在SAP ECC/S4HANA系统中处理供应商主数据时,Table Control控件带来的批量导入挑战。文章指出,银行账户维护界面中Table Control的滚动动作难以被录制工具捕捉,导致批量导入数据时仅能填充前5行。解决方案是通过BDC_OKCODE显式触发翻页(如=P+),并配合行号拼接(如(01)~(05))将数据写入不可见行。文中提供了完整的ABAP程序示例,展示了如何读取外部文件、动态生成BDCDATA、处理表格翻页逻辑,最终通过CALL TRANSACTION执行XK01事

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 277

Java之戳中痛点之 Synchronized 深度解析

点击上方 "程序员小乐"关注,星标或置顶一起成长每天凌晨00点00分,第一时间与你相约每日英文If the heart has no place to perch o...

程序员小乐 280

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Java之戳中痛点之synchronized 深度解析【云图智联】

概览: 简介:作用、地位、不控制并发的影响 用法:对象锁和类锁 多线程访问同步方法的7种情况 性质:可重入、不可中断 原理:加解锁原理、可重入原理、可见性原理 缺陷:效率低、不够灵活、无法预判是否成功获取到锁 如何选择Lock或Synchronized 如何提高性能、JVM如何决定哪个线程获取锁 总结 后续会有代码演示,测试环境 JDK8、IDEA 一、简介 1、作用 能够保证在同一时刻最多只有一个线程执行该代码,以保证并发安全的效果。 2、地位 Synchr...

230

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

mysql 痛点_Java之戳中痛点 - (8)synchronized深度解析

Java之戳中痛点 - (8)synchronized深度解析概览:简介:作用、地位、不控制并发的影响用法:对象锁和类锁多线程访问同步方法的7种情况性质:可重入、不可中断原理:加解锁原理、可重入原理、可见性原理缺陷:效率低、不够灵活、无法预判是否成功获取到锁如何选择Lock或Synchronized如何提高性能、JVM如何决定哪个线程获取锁总结后续会有代码演示,测试环境 JDK8、IDEA一、简介...

weixin_35996612的博客 160

java中对象加锁不具有可重入性_Java之戳中痛点 - (8)synchronized深度解析

概览:简介:作用、地位、不控制并发的影响用法:对象锁和类锁多线程访问同步方法的7种情况性质:可重入、不可中断原理:加解锁原理、可重入原理、可见性原理缺陷:效率低、不够灵活、无法预判是否成功获取到锁如何选择Lock或Synchronized如何提高性能、JVM如何决定哪个线程获取锁总结后续会有代码演示,测试环境 JDK8、IDEA一、简介1、作用能够保证在==同一时刻==最多只有一个线程执行该代码,...

weixin_26870313的博客 135

Synchronized深度解析

Java之戳中痛点 - (8)synchronized深度解析

斯维特哈特 380
上一篇: 2020年平均工资出炉 IT行业超17万居首
下一篇: 【Android进阶】RxJava 堆栈异常信息显示不全,怎么搞?
不秃头de程序猿
博客等级 码龄5年 4089粉丝 1478原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值