【多线程锁】synchronized对象锁与类锁用法

一、概念

1. 概念

​ synchronized是java中的关键字,可以在需要线程安全的业务场景中进行使用,保证线程安全,它是利用锁机制来实现同步的。

2. 特性
  • 原子性:同一时间只允许一个线程持有某个对象锁,对需同步的代码块(复合操作)进行访问,是一种排他的机制,因此被synchronized关键字修饰的同步代码块是无法被中途打断的,能保证代码的原子性

  • 可见性:synchronized关键字包括monitor enter和monitor exit两个JVM命令,它能保证在任何时候任何线程执行到monitor enter成功之前都必须从主内存中获取数据,而不是从缓存中,在monitor exit运行成功之后,共享变量被更新后的值必须刷入主内存。

  • 有序性:synchronized的指令严格遵守java happens-before规则,一个monitor exit指令之前必定要有一个monitor enter;这种顺序性是以程序的串行化执行换来的,在synchronized关键字所修饰的代码块中代码指令也会发生指令重排序的情况

二、用法分类

1. 按照修饰对象
  • 修饰代码块
    • synchronized(this|object) {}
    • synchronized(类.class) {}
  • 修饰方法
    • 修饰非静态方法
    • 修饰静态方法
2. 按照获取的锁分类
  • 获取对象锁
    • synchronized(this|object) {}
    • 修饰非静态方法
  • 获取类锁
    • synchronized(类.class) {}
    • 修饰静态方法
  • 对象锁与类锁的区别
    • 对象锁:每个实例都会有一个monitor对象,即Java对象的锁,类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰
    • 类锁:每个类只有一个Class对象,所以每个类只有一个类锁;类锁是加载类上的,而类信息是存在JVM方法区的,并且整个JVM只有一份,方法区又是所有线程共享的,所以类锁是所有线程共享的

三、代码实战

1. 锁住实体中的非静态变量(对象锁)
@Slf4j
public class SyncThread implements Runnable {

    private final Object lock = new Object();

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (lock) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码

public class SyncTest {

    public static void main(String[] args) {
        SyncThread syncThread = new SyncThread();
        new Thread(new SyncThread(), "thread-1").start();
        new Thread(new SyncThread(), "thread-2").start();
        new Thread(syncThread, "thread-3").start();
        new Thread(syncThread, "thread-4").start();
    }
}

运行结果

在这里插入图片描述

结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问

2. 锁住实体中的静态变量(类锁)
@Slf4j
public class SyncThread implements Runnable {

    private static final Object lock = new Object();

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (lock) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果

在这里插入图片描述

结论:所有实例按照顺序访问

3. 锁住无关对象
@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (new SyncThread()) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果

在这里插入图片描述

结论:所有实例同时访问

4. 锁住this对象(对象锁)
@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (this) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果

在这里插入图片描述

结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问

5. 锁住类对象(类锁)
@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private void syncMethod() {
        log.info("thread in");
        synchronized (SyncThread.class) {
            try {
                log.info("thread start");
                TimeUnit.SECONDS.sleep(2);
                log.info("thread end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果

在这里插入图片描述

结论:所有实例按照顺序访问

6. 锁住非静态方法(对象锁)
@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private synchronized void syncMethod() {
        log.info("thread in");
        try {
            log.info("thread start");
            TimeUnit.SECONDS.sleep(2);
            log.info("thread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

在这里插入图片描述

结论:如果是同一个实例,就会按顺序访问,如果是不同实例,就可以同时访问

7. 锁住静态方法(类锁)
@Slf4j
public class SyncThread implements Runnable {

    @Override
    public void run() {
        syncMethod();
    }

    private synchronized static void syncMethod() {
        log.info("thread in");
        try {
            log.info("thread start");
            TimeUnit.SECONDS.sleep(2);
            log.info("thread end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-57QwoVPX-1657705165771)(C:\Users\y00577086\AppData\Roaming\Typora\typora-user-images\image-20220713172345664.png)]

结论:所有实例按照顺序访问

四、总结

在这里插入图片描述

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值