前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >探讨synchronized关键字

探讨synchronized关键字

作者头像
小四的技术之旅
发布2022-07-26 17:06:13
2060
发布2022-07-26 17:06:13
举报
文章被收录于专栏:小四的技术文章

synchronized作用

能够保证在同一时刻最多有一个线程执行该段代码,以保证并发的安全性。当第一个线程去执行该段代码的时候就拿到锁, 并独占这把锁,当方法执行结束或者一定条件后它才释放这把锁,在没释放锁之前,所有的线程处于等待状态。

探讨synchronized

不控制高并发的后果:定义一个全部变量count,开启一千个线程对count进行相加,为了等所有线程都执行完后再输出count,这里使用了 CountDownLatch来完成,CountDownLatch我们后续会细说,CountDownLatch countDownLatch = new CountDownLatch(1000); 传入的构造函数为1000,每次执行countDownLatch.countDown();计数器都会-1,直到计数器为0,才会执行countDownLatch.await(); 后的代码,countDownLatch.await();就起到阻塞的作用。

代码语言:javascript
复制
public class Test {
    private static Integer count = 0;

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(10000);
        Test test = new Test();
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    test.add();
                    countDownLatch.countDown();
                }
            });
            thread.start();
        }
        countDownLatch.await();
        System.out.println(count);
    }

    public void add() {
        count++;
    }
}

预期值本来是1000,但是没有控制并发,所以结果为986

分析一下原因:

count++,看上去只是一个操作,实际上包含了三个动作

1,从内存中读取count

2,将count加1

3,将count的值写入到内存中

出现结果与预期不一样的原因是当执行完第一个线程的时候,可能第二个线程就进入执行,就被打断了,结果还没有写入到内存中, (如当count为5,第一个线程进入方法,将count+1 ,结果count=6,但是还没有写入内存,第二个线程就进去,它读到的值不是6,而是5, 因为第一个线程还没有将count写入内存),这样是线程不安全的,下面我们使用synchronized来完成线程的同步,保证操作的原子性和可见性。使用synchronized有三种方式

1.代码块锁:将关键字包裹方法体

代码语言:javascript
复制
    public void add() {
        synchronized (this) {
            count++;
        }
    }

2.普通方法锁:synchronized加在方法上

代码语言:javascript
复制
    public synchronized void add() {
        count++;
    }

3.类锁:静态方法锁 静态方法可以直接被类使用,所以实际上对静态方法的同步就是对类的同步,不过值得注意的是,使用类锁, 类锁只能在同一时刻被一个对象拥有,而方法锁可以被多个对象拥有,他们之间互不影响,可以各自运行, 也可以同时运行,而类锁只能是一个对象单独运行,类锁有两种写法,一种是直接将synchronized关键字加在静态 方法上面,另一种则是使用类.class形式

1.静态方法形式

代码语言:javascript
复制
    public static synchronized void add() {
        count++;
    }

2.类.class形式

代码语言:javascript
复制
    public void add() {
        synchronized (Test.class){
            count++;
        }
    }

synchronized原理

被synchronized 关键字修饰的方法、代码块,就是 monitor机制的临界区,进入锁和释放锁是基于monitor来实现的 同步方法和同步代码块, monitor有两个指令,monitorenter会插入到同步代码块的位置, monitorexit会插入到方法块结束和退出的时候, 可能有多个monitorexit对应一个monitorenter,因为退出可以是方法结束或者抛出异常

通过反编译查看

1.使用javac命令将java文件编辑为.class文件
代码语言:javascript
复制
javac SynchronizedTest.java
编译后的内容
2.使用javap命令反编译.class文件
代码语言:javascript
复制
javap -verbose SynchronizedTest.class

monditorenter和monditorexit

实际上一个对象和一个monitor相关联,一个monitorde lock锁只能被一个线程在同一时间获得 monditorenter的三种情况

1.当monitor计数器为0,代表没有被获得,然后线程立马获得该monitor,并把计数器加1,当别的线程想进来,但是看到计数器为1,就代表已经被其他线程占有,就只有等待

2.如果monitor已经拿到了锁的所有权,又重入了锁,则monitor会累加

3.如果monitor被其他线程占用了,当我想去获取的时候,不能获取到,只能处于阻塞状态,当计数器为0才能再次尝试去获取锁

monditorexit的作用是释放锁,前提是要先拥有此锁,原理是将计数器减1,减1如果monitor为0,则对象不再拥有对锁的所有权,就是解锁,如果不是1,则证明是重入进来的,继续持有锁,

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 刘牌 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • synchronized作用
  • 探讨synchronized
    • 分析一下原因:
    • synchronized原理
      • 通过反编译查看
        • 1.使用javac命令将java文件编辑为.class文件
        • 编译后的内容
        • 2.使用javap命令反编译.class文件
      • monditorenter和monditorexit
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档