• Java 线程安全(二)-- synchronized


    第三种情况

    public class Synchronized3Test {
    
        private int x = 0;
    
        private int y = 0;
    
        private String name;
    
        public void runTest() {
    
        }
    
        private synchronized void count(int newValue) {
            x = newValue;
            y = newValue;
        }
    
        private synchronized void minus(int del) {
            x -= del;
            y -= del;
        }
    
        private synchronized void setName(String newName) {
            name = newName;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    如代码所示,三个方法有可能同时被不同线程调用,尤其是加法和减法,同时被调用会导致数据同步异常

    我们使用 synchronized 后,synchronized 保护的不是当前方法,而是方法中的数据

    所以说,一旦用 synchronized 修饰之后,这个类当中的所有 synchronized 方法都不能被同时调用,也就是说一个 synchronized 方法被调用了,剩下的所有的 synchronized 不管跟当前方法中的变量有没有关系,都不能被调用了,为的就是防止出现以上加法和减法同时操作相同的变量时导致的数据不同步的问题

    那么问题来了,我 setName 方法可以很清楚的看到跟另外两个方法没有关系,我想在另外两个方法当中的一个被调用的时候,这个方法也能被同时调用,应该怎么做呢?

    在说解决方案之前,我们先来说一下,为什么会出现这种被 synchronized 修饰后,一个线程只能同时访问一个 synchronized 方法的原因,首先是,刚刚已经说过这样做的好处了,那是如何实现的呢?

    在一个类中,有方法被 synchronized 修饰后,当前这个类就会有一个 monitor,来同时监管所有的 synchronized 修饰的方法,如果有线程正在执行其中一个方法,再有线程想要调用其中任意一个 synchronized 方法的话,都会被 monitor 拒绝,也就是说,所有的 synchronized 修饰的方法共用同一个 monitor。方便理解我们看图
    在这里插入图片描述
    既如此,那我们能不能创建多个 monitor,让彼此保护的数据互相有影响的方法使用一个 monitor,没有关系的使用新的 monitor 呢?

    答案是可以的

    我们修改代码

    public class Synchronized3Test {
    
        .....
    
        private final Object monitor1 = new Object();
        private final Object monitor2 = new Object();
    
        private void count(int newValue) {
            synchronized (monitor1) {
                x = newValue;
                y = newValue;
            }
        }
    
        private void minus(int del) {
            synchronized (monitor1) {
                x -= del;
                y -= del;
            }
        }
    
        private void setName(String newName) {
            synchronized (monitor2) {
                name = newName;
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    如代码所示,我们创建两个标记,monitor1monitor2,让相关的保护内容使用同一个标记,也就是使用同一个 monitor,从而达到我们想要的效果

    在这里插入图片描述
    这里解释下使用 Object,创建标记的原因,因为只起到标记的作用,所以说用任何对象来标记都行,使用 String 也是可以的,我们当然使用最小的对象,没必要用 String 这个更大的对象

    这里解释一下上面 synchronized 不同写法的作用

    private synchronized void count() {
    }
    
    • 1
    • 2

    等同于

    private void count() {
    	synchronized (this) {
    	}
    }
    
    • 1
    • 2
    • 3
    • 4

    他们都是使用的 this 这个 monitor,第一种写法一种简写

  • 相关阅读:
    会话跟踪及常用方法
    E36150系列 自动量程台式电源
    前端项目实战189-根据id转化name
    git GUI的使用&SSH协议&IEDA使用git
    Mybatis传递实体对象只能直接获取,不能使用对象.属性方式获取
    利用go制作微信机器人
    【LeetCode-简单题KMP】459. 重复的子字符串
    开源 Golang 微服务入门一: HTTP 框架 Hertz
    “用爱发电”难以为继?开源还需要真金白银投入
    c++ 获取当前时间(精确至秒、毫秒和微妙)
  • 原文地址:https://blog.csdn.net/qq_35178391/article/details/126041154