• Java并发-synchronized使用方法


    synchronized 关键字介绍

    • Java 中的每个对象都可以把它当作一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为内部锁,也叫作监视器锁。
    • 代码在进入 synchronized 代码块前会自动获取内部锁,这时候其他线程访问该同步代码块时会被阻塞挂起。拿到内部锁的线程会在正常退出同步代码块或者抛出异常后或者在同步块内调用了该内置锁资源的 wait 系列方法时释放该内置锁。
    • 内置锁:即排它锁,也就是当一个线程获取这个锁后,其他线程必须等待该线程释放锁后才能获取该锁。

    Tips:由于 Java 中的线程是与操作系统的原生线程一一对应的,所以当阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作,这是很耗时的操作,而 synchronized 的使用就会导致上下文切换。

     synchronized 的三种使用方式

    Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础。synchronized 的三种使用方式如下:

    • 普通同步方法(实例方法)锁是当前实例对象 ,进入同步代码前要获得当前实例的锁;
    • 静态同步方法锁是当前类的 class 对象 ,进入同步代码前要获得当前类对象的锁
    • 同步方法块:锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

    synchronized 作用于实例方法

    场景设计

    • 创建两个线程,分别设置线程名称为 threadOne 和 threadTwo;
    • 创建一个共享的 int 数据类型的 count,初始值为 0;
    • 两个线程同时对该共享数据进行增 1 操作,每次操作 count 的值增加 1;
    • 对于 count 数值加 1 的操作,请创建一个单独的 increase 方法进行实现;
    • increase 方法中,先打印进入的线程名称,然后进行 1000 毫秒的 sleep,每次加 1 操作后,打印操作的线程名称和 count 的值;
    • 运行程序,观察打印结果。

    1. package jvm.juc;
    2. public class SynchroizedTest extends Thread{
    3. static int count = 0;
    4. public synchronized void increaseCount() {
    5. String thread = Thread.currentThread().getName();
    6. System.out.println("当前" + thread + "线程正在操作");
    7. try {
    8. Thread.sleep(1000);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. count += 1;
    13. System.out.println("当前" + thread + ":" + count);
    14. }
    15. @Override
    16. public void run() {
    17. increaseCount();
    18. }
    19. public static void main(String[] args) {
    20. SynchroizedTest test = new SynchroizedTest();
    21. Thread t1 = new Thread(test);
    22. Thread t2 = new Thread(test);
    23. t1.setName("threadOne");
    24. t2.setName("threadTwo");
    25. t1.start();
    26. t2.start();
    27. }
    28. }

    上述程序运行结果如下图所示

     Tips:仔细看 DemoTest test = new DemoTest () 这就话,我们创建了一个 DemoTest 的实例对象,对于修饰普通方法,synchronized 关键字的锁即为 test 这个实例对象。

     synchronized 作用于静态方法

    暂时其他代码不变,然后main函数里面代码改成如下形式

    1. public static void main(String[] args) {
    2. SynchroizedTest test = new SynchroizedTest();
    3. SynchroizedTest test2 = new SynchroizedTest();
    4. Thread t1 = new Thread(test);
    5. Thread t2 = new Thread(test2);
    6. t1.setName("threadOne");
    7. t2.setName("threadTwo");
    8. t1.start();
    9. t2.start();
    10. }

    代码执行的结果如下图所示

     可以看出,两个线程同时进入到锁中

    两个线程持有两个不同的锁,不会产生互相 block。相信讲到这里,同学对实例对象锁的作用也了解了,那么我们再次将 increase 方法进行修改,将其修改成静态方法,然后输出结果。

     结果如下,又得到了顺序执行

     结果分析:我们看到,结果又恢复了正常,为什么会这样?
    关键的原因在于,synchronized 修饰静态方法,锁为当前 class,即 DemoTest.class。

    synchronized 作用于同步代码块 

    Tips:对于 synchronized 作用于同步代码,锁为任何我们创建的对象,只要是个对象即可,如 new Object () 可以作为锁,new String () 也可作为锁,当然如果传入 this,那么此时代表当前对象。

    1. /**
    2. * synchronized 修饰实例方法
    3. */
    4. static final Object objectLock = new Object(); //创建一个对象锁
    5. public static void increase() throws InterruptedException {
    6. System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程在我执行完毕之前,不可进入。" );
    7. synchronized (objectLock) {
    8. sleep(1000);
    9. count++;
    10. System.out.println(Thread.currentThread().getName() + ": " + count);
    11. }
    12. }

     代码解析:我们创建了一个 objectLock 作为对象锁,除了第一句打印语句,让后三句代码加入了 synchronized 同步代码块,当 threadOne 进入时,threadTwo 不可进入后三句代码的执行。

     

     

  • 相关阅读:
    基于简化的评分卡、Smote采样和随机森林的信贷违约预测
    安装k8s集群
    EfficientViT:高效视觉transformer与级联组注意力
    Linux openGauss 数据库远程连接
    Python和Nose实现移动应用的自动化测试
    搭建STM32开发环境
    多壁碳纳米管-室温离子液体1-丁基-3-甲基咪唑六氟磷酸盐([EMIM]PF6)修饰辣根过氧化物酶(HRP)|新型碳糊酶电极(HRP-MWCNTs-CILE)
    Docker推送镜像错误
    JavaScript实现排序算法
    /bin/bash: Resource temporarily unavailable
  • 原文地址:https://blog.csdn.net/abc123mma/article/details/128060729