JUC系列(五)| Synchonized关键字的进一步讲解

简介: JUC系列(五)| Synchonized关键字的进一步讲解

微信截图_20220525182209.png


多线程一直Java开发中的难点,也是面试中的常客,趁着还有时间,打算巩固一下JUC方面知识,我想机会随处可见,但始终都是留给有准备的人的,希望我们都能加油!!!

沉下去,再浮上来,我想我们会变的不一样的。

synchronized 实现同步的基础:Java 中的每一个对象都可以作为锁。


一次偶然在家阳台上拍下来的,喜欢这样的天,让人舒缓


JUC系列

正在持续更新中...


一、对于普通同步方法:


对于普通同步方法,锁的是当前实例对象


一个对象里面如果有多个 synchronized非静态方法,某一个时刻内,只要一个线程去调用了其中的 一个用synchronized修饰的方法, 其它的线程都只能等待。换句话说,某一个时刻内,只能有唯一一个线程去访问这些 synchronized 方法。


(注:当只有一个对象实例时)。


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(()->{
                demo.test1();
        },"AA").start();
        new Thread(()->{
                demo.test2();
        },"BB").start();
    }
}
class Demo{
    public synchronized void test1(){
        try {
            Thread.sleep(1000);
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+":: 循环第"+i+"次");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public synchronized void test2(){
        System.out.println(Thread.currentThread().getName()+":: 只循环一次的方法");
    }
}


运行结果:


微信截图_20220525182338.png


通过运行结果我们看到,当A线程进入的由synchronized修饰的test1()的方法后,B线程只有等待A线程释放锁,才能进入由synchronized修饰的test2(),以此可以说明当只有一个实例对象时,一个对象里面如果有多个 synchronized非静态方法,某一个时刻内,只要一个线程去调用了其中的 一个用synchronized修饰的方法, 其它的线程都只能等待


原因因为锁的是当前对象 this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized 方法

上面test1()的方法代码也可以换成这样,结果也是一样的。


public void test1() {
    synchronized (this) {
        try {
            Thread.sleep(1000);
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


补充

1、但是加个普通方法,是和同步锁无关的。


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(() -> {
            demo.test1();
        }, "AA").start();
        new Thread(() -> {
            demo.test2();
        }, "BB").start();
        new Thread(() -> {
            demo.test3();
        }, "CC").start();
    }
}
class Demo {
    public void test1() {
        synchronized (this) {
            try {
                Thread.sleep(1000);
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void test2() {
        System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
    }
    public void test3(){
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"::没有synchronized关键字的普通方法");
        }
    }
}


运行结果:


微信截图_20220525182430.png


结论:另外加一个普通方法,执行起来是完全和同步锁无关的,也无需等待。


2、另外如果换成两个对象,情况也会不一样


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(() -> {
            demo.test1();
        }, "AA").start();
        Demo demo1 = new Demo();
        new Thread(() -> {
            demo1.test2();
        }, "BB").start();
    }
}
class Demo {
    public void test1() {
        synchronized (this) {
            try {
                Thread.sleep(1000);
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void test2() {
        System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
    }
}


运行结果:


微信截图_20220525182509.png


无需等待A线程释放锁,即可执行。


小结


即如果一个实例对象的非静态同步方法得到锁之后,此实例对象的其他非静态同步方法必须一直等到获取到锁的方法释放锁之后,才能重新获取到锁。但是另外的实例对象的非静态同步方法和此实例对象用的并非一把锁,所以无需等待此实例对象已经获取到的锁的非静态同步方法释放锁就可以去获取属于他们的锁。


下面看看静态同步方法是什么样的吧👇


二、对于静态同步方法:


对于静态同步方法,锁是当前类的 Class 对象


例如:我们修改一下上面的那个例子。把test2()方法改成静态方法,看看结果是什么样的吧。


public static synchronized void test2() {
    System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
}


运行结果:


QQ截图20220525182552.png


所有的静态同步方法用的也是同一把锁——类对象本身,而非静态同步方法锁的当前实例对象,所以这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会产生竞态条件的


和之前一样,如果一个静态同步方法获取到锁之后,其他的静态同步方法都必须等待此方法释放锁之后,才能继续获取到锁。并且不管是一个实例对象的静态同步方法之间,还是不同的实例对象静态同步放之间,他们都必须遵守,因为他们是同一个类的实例对象。


此处代码书写方式勿怪,为测试刻意而为。


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(() -> {
            demo.test1();
        }, "AA").start();
        Demo demo1 = new Demo();
        new Thread(() -> {
            demo1.test2();
        }, "BB").start();
    }
}
class Demo {
    public static synchronized void test1() {
        try {
            Thread.sleep(1000);
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static synchronized void test2() {
        System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
    }
}


三、对于同步方法块:


对于同步方法块,锁是 Synchonized 括号里配置的对象


当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。


测试一: 当锁的对象相同时,产生竞争条件,形成竞争状态。


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(()->{
            demo.test4();
        },"CC").start();
        new Thread(()->{
            demo.test5();
        },"DD").start();
    }
}
class Demo {
    public void test4()  {
        synchronized (Demo.class){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
            }
        }
    }
    public void test5(){
        synchronized (Demo.class){
            System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
        }
    }
}


微信截图_20220525182650.png


测试二:当锁的对象不同时,无须等待test4()方法释放锁,即可执行test5()方法,

原因:因为不是同一把锁,不产生竞争条件。


public class Synchronized8Demo {
    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        new Thread(()->{
            demo.test4();
        },"CC").start();
        new Thread(()->{
            demo.test5();
        },"DD").start();
    }
}
class Demo {
    public void test4()  {
        synchronized (this){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
            }
        }
    }
    public void test5(){
        synchronized (Demo.class){
            System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
        }
    }
}


小结


对于同步方法块,关键就是 synchronized () 括号中锁的对象是谁


四、自言自语


最近又开始了JUC的学习,感觉Java内容真的很多,但是为了能够走的更远,还是觉得应该需要打牢一下基础。


最近在持续更新中,如果你觉得对你有所帮助,也感兴趣的话,关注我吧,让我们一起学习,一起讨论吧。


你好,我是博主宁在春,Java学习路上的一颗小小的种子,也希望有一天能扎根长成苍天大树。


希望与君共勉😁


我们:待别时相见时,都已有所成



目录
相关文章
|
5月前
|
缓存 Java 编译器
必知的技术知识:Java并发编程:volatile关键字解析
必知的技术知识:Java并发编程:volatile关键字解析
27 0
|
6月前
|
缓存 Java
5个案例和流程图让你从0到1搞懂volatile关键字
5个案例和流程图让你从0到1搞懂volatile关键字
|
Java
关于关键字volatile的一二
关于关键字volatile的一二
79 0
深入理解JUC:第三章:AtomicReference原子引用
深入理解JUC:第三章:AtomicReference原子引用
174 0
深入理解JUC:第三章:AtomicReference原子引用
|
编译器
C零散知识点汇总之volatile关键字
C零散知识点汇总之volatile关键字
|
调度 uml
AQS 都看完了,Condition 原理可不能少!
在介绍 AQS 时,其中有一个内部类叫做 ConditionObject,当时并没有进行介绍,并且在后续阅读源码时,会发现很多地方用到了 Condition ,这时就会很诧异,这个 Condition 到底有什么作用?那今天就通过阅读 Condition 源码,从而弄清楚 Condition 到底是做什么的?当然阅读这篇文章的时候希望你已经阅读了 AQS、ReentrantLock 以及 LockSupport 的相关文章或者有一定的了解(当然小伙伴也可以直接跳到文末看总结)。
96 0
|
存储 Java 调度
一网打尽!synchronized关键字入门(一)
一网打尽!synchronized关键字入门(一)
一网打尽!synchronized关键字入门(一)
|
存储 Java C++
一网打尽!synchronized关键字入门(二)
一网打尽!synchronized关键字入门(二)
一网打尽!synchronized关键字入门(二)
|
存储 安全 Java
一网打尽!synchronized关键字入门(三)
一网打尽!synchronized关键字入门(三)
一网打尽!synchronized关键字入门(三)
|
安全 Java Spring
重点丨什么是双重检查锁模式?以及为何需要 volatile 关键字?
双重检查锁定(Double check locked)模式经常会出现在一些框架源码中,目的是为了延迟初始化变量。这个模式还可以用来创建单例。下面来看一个 Spring 中双重检查锁定的例子。
重点丨什么是双重检查锁模式?以及为何需要 volatile 关键字?