关于synchronized的参数及其含义

 更新时间:2021年10月12日 09:47:21   作者:知我饭否  
这篇文章主要介绍了synchronized的参数及其含义详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

这个想必大家都不陌生,java里面的重量级锁。用来控制线程安全的。在long And long ,我刚开始接触java的时候 ,我就对这个关键词好奇颇深。尤其是 它的参数,有this的 也有静态变量的。网上对这个参数解释又太过术语话。

例如

作用于方法时,锁住的是对象的实例(this);

当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带PermGen(jdk1.8则是metaspace),永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;

当作用于一个静态类的时候,不管是不是本身内部的静态类,还是别人的静态类,都可以完成锁住的效果(ps 上锁的时候,相当于一群人 拿把锁找个东西上锁

synchronized作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。

这不管是对于初学者,还是老鸟 。都感觉沉长无味。

最近,我的项目涉及到这一块,我对这个方法有了顿悟,写下来 传之后世

我对这个参数的理解是这样的。

synchronized(获取锁的地方){
工作内容
}

是的 ,你们没看错 ()里面的就是一个“获取锁的地方”。{ }里面是工作内容。 他们有这几条关系。

  • 获取锁的地方 和 工作内容 是不必需要 有任何关联的。 可以有关联,但没有关联 也没事
  • 获取锁的地方 :只是一个让synchronized 整体 放置锁的地方,一个“获取锁的地方” 只有一把锁。**
  • 工作内容: 要进行工作的**前提是 synchronized整体,找到一个“获取锁的地方”,在 “获取锁的地方” 获取到了锁, 如果 “获取锁的地方”的锁,已经被别人拿去了,那么就只能等待别人 把锁还给 “获取锁的地方”。然后 再获取锁。
  • synchronized 会使用运行工作内容的前提,必须获取到锁。这个意思呢,就是例如,当前线程synchronized 获取到了objec A的锁,那么其他线程还可以更改 A 进行操作么?。 这个得到分两种情况,如果其他线程代码 是被synchronized 所包裹的,那么只能等 objec A被释放了,才能更改。如果,其他线程代码没有被synchronized包裹,她可以不需要有锁,就可以对objec A进行操作。

解释

先打个比方,我们把 synchronized这个看成一个整体,那么在多线程的时候,就会有很多个 synchronized整体这个整体开始工作的前提是 它可以从 “获取锁的地方”拿到锁。那么他就可以做工作了。如果 想要 “获取锁的地方”的锁,已经被别人拿走了。那么只能等别人把锁换回来,才行。

上面这个还是有点抽象。就这么说吧 在程序运行多线程的,会产生好多synchronized整体。就叫小明,小红,小芳,小花…等。他们现在手中没有锁,只有手中有锁的时候 才可以工作。 现在小明,小红他们来到工作的地方。获取锁的地方就是路边的一棵树::注意现在路边只有这一棵树小红抢先一步将树上的锁拿走了。然后小红工作去了(她的工作 不用和树有关系,她可以砍树,也可以去路边扫地 )。小明,小芳等其他人,因为没地方获取锁(只有一棵树 并且树的锁 已经被小红拿走了)。所以只能在原地等待 ,小红工作做完了 把锁还给树 ,小明,小芳才有机会进行工作。

注意: 上面是路边只有一棵树,如果存在好几颗树的话,那么小明,小红他们就可以同时放置获取锁 一个数对应一个锁,多个树就多个锁。这个对应于类的实例 (实例可以有好多个)

在这里插入图片描述

验证

我们首先定义一个Runnable ,因为 多线程的话 最好还是用Runnable,至于为什么呢?百度去。

  static class LockRunable implements Runnable {
        private int num;
        private LockNormal lockNormal;
        public LockRunable(int num, LockNormal lockNormal) {
            this.num = num;
            this.lockNormal = lockNormal;
        }
        @Override
        public void run() {
            while (true) {

                    if (num <= 0) {
                        break;
                    }
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    " + num--);

            }
        }
    }

这个类呢 接受一个整数,一个LockNormal我自己定义的类,在run里面呢先对 num进行判断小于等于0的话,就退出,否则减一。 运行一下

  LockNormal lockNormal = new LockNormal();
        LockRunable one = new LockRunable(15, lockNormal);
        new Thread(one).start();
        new Thread(one).start();
        new Thread(one).start();
        new Thread(one).start();
        new Thread(one).start();

在这里插入图片描述

num竟然变成负的了,这是因为 五个线程同时对他进行操作,造成的。要避免这种情况,只能一个线程操作的时候,别的线程就不能操作。

我们给程序加锁试一下。首先定义一个 静态成员,静态成员有一个特点是系统初始化的,全局只有一个实例。就相当于上面的 只有一棵树。

static final transient Object lock = new Object();

对 工作内容加锁

    @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    if (num <= 0) {
                        break;
                    }
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    " + num--);
                }
            }
        }
    }

运行

在这里插入图片描述

这次就和预期结果相等了。但是 我们可发现 都是都是同一个线程进行操作的,这是因为 sleep时候不会释放锁,其他线程也无法进行操作

传入实例

    static class LockRunable implements Runnable {

        private int num;

        private LockNormal lockNormal;

        public LockRunable(int num, LockNormal lockNormal) {
            this.num = num;
            this.lockNormal = lockNormal;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lockNormal) {
                    if (num <= 0) {
                        break;
                    }
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "    " + num--);
                }
            }
        }
    }

结果:

在这里插入图片描述

也是同样的结果,如果 我们传入this的话 运行还是一样的结果。这就是上面树的问题了,不管是传入的实例,还是this本身实例。只有能保证它们本身是唯一的,也就是“获取锁的地方”只有一个,同一时间内只有一个人能成功获取到锁 进行工作。就和锁静态成员是一样的效果如果我们 new 两个Runnable的话,并且传入this实例的话,就会有两个“获取锁的地方” ,可以有两个人同时工作。

总结

synchronized(放锁的地方){
工作内容
}

只要我们保证“获取锁的地方”是唯一的,那么在同一时刻,就只能有一个工作内容会被执行。如果,“获取锁的地方”不是唯一的(一个类new很多实例,锁住this实例,那么同一时刻就会有很多放锁的地方),在同一时刻就会有好多 工作内容被执行。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot中的@Inherited注解详解

    SpringBoot中的@Inherited注解详解

    这篇文章主要介绍了SpringBoot中的@Inherited注解详解,@Inherited是一个标识,用来修饰注解,如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解,需要的朋友可以参考下
    2023-08-08
  • 详解Java8中的lambda表达式、::符号和Optional类

    详解Java8中的lambda表达式、::符号和Optional类

    这篇文章主要介绍了Java8中的lambda表达式、::符号和Optional类,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Java深入分析了解平衡二叉树

    Java深入分析了解平衡二叉树

    平衡二叉树又被称为AVL树(有别于AVL算法),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。本文将详解介绍一下平衡二叉树的原理与实现,需要的可以参考一下
    2022-06-06
  • SpringBoot借助spring.factories文件跨模块实例化Bean

    SpringBoot借助spring.factories文件跨模块实例化Bean

    这篇文章主要介绍了SpringBoot借助spring.factories文件跨模块实例化Bean,文章围绕主题展开详细的内容介绍,需要的小伙伴可以参考一下
    2022-04-04
  • springboot拦截器过滤token,并返回结果及异常处理操作

    springboot拦截器过滤token,并返回结果及异常处理操作

    这篇文章主要介绍了springboot拦截器过滤token,并返回结果及异常处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • Java时间处理第三方包Joda Time使用详解

    Java时间处理第三方包Joda Time使用详解

    这篇文章主要为大家介绍了Java时间处理第三方包Joda Time使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Java基于JDBC连接数据库及显示数据操作示例

    Java基于JDBC连接数据库及显示数据操作示例

    这篇文章主要介绍了Java基于JDBC连接数据库及显示数据操作,结合实例形式分析了Java使用jdbc进行mysql数据库连接与数据读取、显示等相关操作技巧,需要的朋友可以参考下
    2018-06-06
  • spring cloud 分布式链路追踪的方法

    spring cloud 分布式链路追踪的方法

    这篇文章主要介绍了spring cloud 分布式链路追踪的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • java统计字符串单词个数的方法解析

    java统计字符串单词个数的方法解析

    在一些项目中可能需要对一段字符串中的单词进行统计,本文在这里分享了一个简单的demo,有需要的朋友可以拿去看一下
    2017-01-01
  • mybatis一对多方式实现批量插入

    mybatis一对多方式实现批量插入

    这篇文章主要介绍了mybatis一对多方式实现批量插入,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11

最新评论