一文搞懂Synchronized

简介:对多线程可见,保证同一时刻最多只有一个线程执行代码,达到线程并发安全,被修饰的代码保证了可见性、一定程度上的原子性和禁止指令重排序。关键是独占一个锁。关键字,java原生支持,最基本的互斥同步手段。

如果不使用并发手段的后果:比如两个线程同时执行a++,最后结果比预期的要少

原因:a++包含三个操作不具有原子性,读取a值,a+1,然后写入内存,共分三个步骤抢占式调度引起,底层原理是java内存模型机制引起。

用法:

对象锁:方法锁(默认对象为this当前实例对象)和同步代码块锁(自己指定锁对象包括this)

类锁:指Synchrinized修饰的静态方法或者代码块指定锁为Class对象,java类可以用很多对象,只有一个Class对象

锁不同相互没有影响--并行而非串行

类锁可以全局保护,不同的对象实例进来也可以同步执行如New Thrend(class.this1)和

New Thrend(class.this2),同时执行同步代码块,对象锁是并行的而类锁可以同步执行

 

多线程访问同步方法的七种情况

1:两个线程同时访问一个对象的同步方法

       同一把锁存在阻塞

2:两个线程访问来两个对象的同步方法

       锁住不同的实例,所以不会阻塞

3:两个线程访问的是静态方法

       锁对象为Class对象,存在阻塞

4:如果同时访问同步方法和非同步方法

       非同步方法不会受到影响

5:访问同一个对象的不同的普通同步方法

      锁是一样的,发生阻塞

6:同时访问静态和非静态的同步方法

      两个锁不一样,不受影响,串行

7:方法抛出异常,会释放锁

      总结:

          a:一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待

          b:每一个实例都对应有自己的一把锁,不同的实例之间相互不影响,但是Class对象锁,所有对象公用一把锁

          c:无论正常执行完毕还是抛出异常都会释放锁

Synchrinized性质:

   1.可重入(递归锁):同一线程外层函数获得锁之后,内层函数可以直接再获取该锁,避免死锁,提升封装性,粒度:线程而非调用

  2. 独占

    可重入性质:

      1:   同一个方法可重入

      2:可重入不要求是同一个方法

      3:可重入不要求是同一个类

  不可中断:一旦锁已经被别人获得,,如果我想再想获得,就只能等待或者阻塞,直到别的线程释放锁,否则会一直等待下去(与Lock比,可以设置超时时间)

                                                                   原理

1:加锁和释放锁

现象

     内置锁

     等价代码----Lock

     深入JVM字节码:

                        概况:java对象头存储synchrinized,基于monitor

                        反编译:monitorenter开始锁住代码块,monitorexit释放锁

                        Monitorenter:+1获取锁,重入再+1,其他线程阻塞

                        Monitorexit:计数器-1,为0就失去monitor的所有权,其他线程可以获取可以获取锁

2:可重入原理:每一个对象都有一把琐,jvm负责跟踪对象被加锁的次数

              线程第一次给对象加锁的时候,计数器变为1,每当相同的线程此对象上再次获得锁,计数器会递增

              每当任务离开的时候,计数递减,每当计数为0的时候,锁被完全释放

3:保证可见性原理:

                Java内存模型:1:主内存数据复制副本到工作内存,2更新,3更新后把数据写入主内存中,一旦被synchrinized修饰,同时只有一个线程修改数据,所以修改对其他线程是可见的,保证线程安全性

缺陷:

           效率低:所得释放情况少,只有当前线程执行结束和发生异常才释放锁,试图获得时不能设定超时时间,不能中断一个正在试图获得锁的线程---Lock对比

           不够灵活(读写锁更加灵活):加锁和释锁的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的

           无法知道是否成功的获取了锁                                                        

 ---Lock接口对比

           Lock lock =new reenetrylock

           lock.lock()

           lock.unLock()

           lock.tryLock() ----返回boobean

           lock.trylock(超时时间)----返回boobean

思考:

       1:使用注意点:锁对象不能为空、作用域不宜过大(效率低)、避免死锁

       2:如何选择sync和Lock?

            建议:如果可以都不使用,使用并发包中的类,考虑开发效率,出错机率以及执行效率

       3:多线程访问同步方法的各种具体情况

思考:

1:多个线程等待同一个synchronized锁的时候,JVM如何选择下一个获取锁的线程,涉及锁的调度机制

2:sync使得同时只有一个线程可以执行,性能差,如何提高性能,优化锁的范围尽可能小,

可以替代,比如读写锁

3:更加灵活的控制锁的获取和释放?

4:什么是锁的升级、降级?什么是JVM里的轻量级锁、偏斜锁、重量级锁?  

总结:

一句话介绍synchronized:

            Jvm会自动通过使用monitro来加锁和解锁,使锁住的代码块具有原子性操纵,保证只有一个线程可以执行指定的代码,从而保证线程安全,同时具有可重入和不可中断的性质   

ps:写的不够好,如果有不足或者错误的地方欢迎留言指正  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章