Java线程基础之Synchronized

本文详细介绍了Java中的Synchronized关键字,包括其作用、不同用法(对象锁、类锁和代码块锁)、多线程并发情况,以及Synchronized的可重入性和不可中断性质,帮助读者掌握并发编程的关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是Synchronized

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

二、Synchronized的作用

  1. 原子性:确保线程互斥地访问同步代码;
  2. 可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的“对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
  3. 有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”;

三、Synchronized主要有三种用法

1.修饰实例方法:作用于当前实例加锁 (对象实例锁)

public synchronized void method() {
    //业务代码
}

2.修饰静态方法:作用于当前类对象加锁 (类锁)

也就是给当前类加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁。因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管 new 了多少个对象,只有一份)

 public static synchronized void  method() {
  //业务代码
}

3.修饰代码块:指定加锁对象,对给定对象加锁 (对象锁)

表示进入同步代码库前要获得给定对象的锁

synchronized (this){
}

表示进入同步代码库前要获得当前class加锁

synchronized (Demo.class){
}

4.总结

根据上述举例我们可以把锁分为两个用法
对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
分为方法锁住和代码块锁
类锁:修饰静态代码的方法或者指定锁为class对象
分为静态锁和class对象

四、代码块锁的使用演示

package synchronizeddemo;
/**
* @author: XXX
* @date: 2023-04-20 15:38
*/
public class demo2 implements  Runnable{
    static demo2 demo2=new demo2();
    // Object lock=new Object
    @Override
    public void run() {
        // 这里可以不用对象,可以自定义一个对象 lock
        synchronized(this){
            System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
        }
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(demo2);
        Thread thread1=new Thread(demo2);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述

五、方法锁的使用演示

package synchronizeddemo;
/**
 * 方法锁的演示
 * @author: XXX
 * @date: 2023-04-20 15:59
 */
public class demo3  implements  Runnable{
    static demo3 demo3=new demo3();
    @Override
    public void run() {
        method();
    }
    public  synchronized void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(demo3);
        Thread thread1=new Thread(demo3);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述

六、类锁的使用演示

只有一个Class对象:Java类可能会有很多个对象,但是只有1个Class对象。
本质:所以所谓的类锁,不过是Class对象的锁而已
用法和效果: 类锁只能在同一时刻被一个对象拥有

形式1:synchronized加在static方法上

package synchronizeddemo;
/**
 * @author: chengen
 * @date: 2023-04-21 11:16
 */
public  class nynchroizedClassStatic  implements Runnable{
    static nynchroizedClassStatic nynchroizedClassStatic0=new nynchroizedClassStatic();
    static nynchroizedClassStatic nynchroizedClassStatic1=new nynchroizedClassStatic();
    @Override
    public void run() {
        method();
    }
    public  synchronized void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(nynchroizedClassStatic0);
        Thread thread1=new Thread(nynchroizedClassStatic1);
        thread0.start();
        thread1.start();
    }
}

在这执行上面的代码会出现thread0和thread1同时执行,因为是实例了两个对象,两个对象去请求里插入图片描述
执行上面的代码会出现thread0和thread1同时执行,因为是实例了两个对象,两个对象去请求

package synchronizeddemo;
/**
 * @author: chengen
 * @date: 2023-04-21 11:16
 */
public  class nynchroizedClassStatic  implements Runnable{
    static nynchroizedClassStatic nynchroizedClassStatic0=new nynchroizedClassStatic();
    static nynchroizedClassStatic nynchroizedClassStatic1=new nynchroizedClassStatic();
    @Override
    public void run() {
        method();
    }
    public static synchronized void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(nynchroizedClassStatic0);
        Thread thread1=new Thread(nynchroizedClassStatic1);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述
在method方法上加上static修饰,就会出现锁的效果,这是因为nynchroizedClassStatic0和nynchroizedClassStatic0都拥有一个唯一的类

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

package synchronizeddemo;
/**
 * @author: chengen
 * @date: 2023-04-21 11:28
 */
public  class synchroizedClassClass implements Runnable{
    static synchroizedClassClass synchroizedClassClass0=new synchroizedClassClass();
    static synchroizedClassClass synchroizedClassClass1=new synchroizedClassClass();
    @Override
    public void run() {
        method();
    }

    public void method(){
        synchronized (this){
            System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
        }
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(synchroizedClassClass0);
        Thread thread1=new Thread(synchroizedClassClass1);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述
执行以上代码会出现thread0和thread1同时执行的情况,因为代码块中的this指的是当前的对象,也就是synchroizedClassClass0,和synchroizedClassClass1

package synchronizeddemo;
/**
 * @author: chengen
 * @date: 2023-04-21 11:28
 */
public  class synchroizedClassClass implements Runnable{
    static synchroizedClassClass synchroizedClassClass0=new synchroizedClassClass();
    static synchroizedClassClass synchroizedClassClass1=new synchroizedClassClass();
    @Override
    public void run() {
        method();
    }
    public void method(){
        synchronized (synchroizedClassClass.class){
            System.out.println("【"+Thread.currentThread().getName()+"】开始执行");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("【"+Thread.currentThread().getName()+"】结束执行");
        }
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(synchroizedClassClass0);
        Thread thread1=new Thread(synchroizedClassClass1);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述
把代码块中的this改成synchroizedClassClass.class这样就是对synchroizedClassClass加锁

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

1.两个线程同时访问一个对象的同步方法是串行还是并行

答案:串行,因为是一个对象同步方法拿的this

2.两个线程访问的是两个对象的同步方法是串行还是并行

答案:并行,因为this代表的当前对象,除非这个同步方法中是类锁也就是class锁

3.两个线程访问的是synchronized的静态方法是串行还是并行

答案:串行,这是因为如果使用静态修饰方法就上升为类锁

4.同时访问同步方法与非同步方法是串行还是并行

答案:并行,一个线程访问同步方法另外一个线程访问同步方法,所以会同时进行

package synchronizeddemo;
/**
 * @author: XXX
 * @date: 2023-05-04 11:14
 */
public class demo4 implements Runnable{
    static demo4 demo4=new demo4();
    public static synchronized void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"加锁的方法开始执行");
        System.out.println("【"+Thread.currentThread().getName()+"】"+"加锁的方法结束执行");
    }
    public static  void method1(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"没有加锁的方法开始执行");
        System.out.println("【"+Thread.currentThread().getName()+"】"+"没有加锁的方法结束执行");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            method();
        }else{
            method1();   
        }
    }
    public static void main(String[] args) {
        Thread thread0=new Thread(demo4);
        Thread thread1=new Thread(demo4);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述

5.访问同一个对象不同的普通(非static)同步方法

答案:串行,因为访问method和method1都是一个this,这个this就是 demo5

package synchronizeddemo;

/**
* @author: XXX
* @date: 2023-05-04 11:14
*/
public class demo5 implements Runnable{


    static demo5 demo5=new demo5();


    public  synchronized void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method加锁的方法开始执行");
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method加锁的方法结束执行");
    }


    public   synchronized void method1(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method1加锁的方法开始执行");
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method1加锁的方法结束执行");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            method();
        }else{
            method1();
        }
    }

    public static void main(String[] args) {
        Thread thread0=new Thread(demo5);
        Thread thread1=new Thread(demo5);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述

6.同时访问静态synchronized和非静态synchronized方法

答案:并行,因为method是类锁拿到的class,而method1拿到的this锁当前对象的锁互不影响。

package synchronizeddemo;

/**
* @author: XXX
* @date: 2023-05-04 11:14
*/
public class demo6 implements Runnable{


    static demo6 demo6=new demo6();


    public    synchronized static void method(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method加锁的静态方法开始执行");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method加锁的静态方法结束执行");
    }


    public   synchronized void method1(){
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method1加锁的非静态方法开始执行");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("【"+Thread.currentThread().getName()+"】"+"method1加锁的非静态方法结束执行");
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread-0")){
            method();
        }else{
            method1();
        }
    }

    public static void main(String[] args) {
        Thread thread0=new Thread(demo6);
        Thread thread1=new Thread(demo6);
        thread0.start();
        thread1.start();
    }
}

在这里插入图片描述

7.抛出异常后是否会自动释放锁

答案:会

八、Synchronized的性质

1.可重入

可重入指的是同一线程的外层函数获得锁以后,内层函数可以直接再次获取该锁。
好处:避免死锁、提升封装性

2.不可中断

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值