Java中的synchronized

前言    (首先要了解各种锁的内部原理)

1. 乐观锁 vs 悲观锁:   Java中的机制就是锁的实现者先来一波预测,如果预测下来发生锁竞争的概率不大,就判定此时是一个乐观锁,如果判定发生锁竞争的概率大,此时就是一个悲观锁。(这两种锁最终做的事情是不一样的,乐观锁做的工作要少,效率是比较高的;悲观锁做的工作比较多,效率是比较低的)     注:但是不绝对,主要掌握概念。
2. 轻量级锁vs重量级锁:   轻量级锁加锁解锁的过程更高效,重量级锁的加锁解锁过程效率更低。一般情况下,乐观锁就是一把轻量级锁,悲观锁就是一把重量级锁,但也是不绝对的。
3. 自旋锁vs等待挂起锁:   自旋锁是轻量级锁的一种典型的实现,等待挂起锁是重量级锁的一种典型实现。自旋锁:当产生锁竞争时,此时自旋锁会一直循环判断,看是否有机会获取锁,会一直忙等,消耗CPU资源,当锁被释放时,会第一时间拿到锁。    等待挂起锁:产生锁竞争时,不会进入忙等状态,不会消耗CPU资源,会趁此机会做点别的事情,而不是一直忙等下去,当锁被释放时,不会第一时间拿到锁,可能会等很久才会拿到锁。(自旋锁通常是纯用户态的,不需要经过内核态,而等待挂起锁是需要经过内核态的,所以时间会可能会很久)
4. 互斥锁vs读写锁:   互斥锁就是正常的加锁解锁,没有更细节的实现了。读写锁是分了三步操作:1.给读加锁  2.给写加锁   3.解锁。区别就是一个只是单纯的加锁,另一个则是把给读加锁和给写加锁分开,如果多线程读同一个变量,是没有线程安全问题的,如果多个线程同时涉及到读和写就会有线程安全问题,多线程同时写也会有线程安全的问题,所以读写锁约定:多线程读一个变量,不会产生阻塞,但是后两种情况就会产生阻塞。所以读写锁用于一读多写的情况(即使阻塞,也不会影响很长的时间)。
5. 可重入锁vs不可重入锁:   如果同一个线程同时加锁两次,进入死锁状态就是不可重入锁,不会进入死锁状态就是可重入锁。(关于死锁的解答可以参考我的其他文章)
6. 公平锁vs非公平锁:   如何定义公平,不是等概率竞争就是公平,而是有先来后到的顺序,按照顺序来才是公平的。所以当一个线程产生锁竞争,进入阻塞等待时,另一个线程过了一会也来等待了....此时两个等待的进程谁先获取到锁呢?如果是先来的进程获取到了锁,就是公平锁,反之则不是公平锁。

1.synchronized的特性

(1)既是乐观锁,也是悲观锁。
(2)既是轻量级锁,也是重量级锁。
(3)轻量级锁基于自旋锁实现,重量级锁基于挂起等待锁实现
(4)不是读写锁。
(5)是可重入锁。
(6)是非公平锁。

     前两个是很好解释的,就是说synchronized是会自适应的,根据锁竞争的激烈程度,来决定synchronized当前是什么锁。开始先是乐观锁,如果锁冲突激烈,就转换为悲观锁。开始是轻量级锁,如果锁被持有的时间较长,就变成重量级锁,轻量级锁通常是基于自旋锁实现的,重量级锁通常是基于等待挂起锁实现的。(后面的是定义记住即可)

2.synchronized的使用

使用也很简单,如下代码:如果进了synchronized代码块就开始进行加锁,出了代码块就解锁。

public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Object object = new Object();
        //搞两个线程,两个线程分别队这个counter进行自增 50000次
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                synchronized (object) {//需要是对同一个对象进行加锁才能产生效果()中就是一个锁对象
                    counter.add();
                }
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                synchronized (object) {
                    counter.add();
                }
            }
        });
        t1.start();
        t2.start();

     上述代就是两个线程并发执行对counter自增50000次,涉及到add操作是3个CPU指令,不是原子的操作,所以对add操作进行加锁变成原子操作。

3.synchronized的锁机制

锁机制

     啥是偏向锁,这里就涉及到加锁成本问题,非必要,不加锁。如果没有人来获取这把锁,我就先加个标签,说我要获取这把锁了哈,其实我并没有进行加锁这个操作,只是进行了加标签操作,然后进入观察状态,看看有没有线程来和我进行锁竞争,如果有,我就卡卡加上锁,如果没有线程来竞争,我就只是做了一步加标签的操作。毕竟能躺着谁愿意站起来呢~~

       然后有了锁竞争,就升级为自旋锁,一个线程获取到了锁,另一个进入自旋状态,注意,自旋锁是很快(一个释放了锁,另一个线程能够立马拿到锁),但是自旋锁是一个while过程(循环的非常快,一直忙等)非常耗费CPU,如果锁竞争的更激烈了,来了10个线程都竞争同一把锁,那都进行自旋状态不现实啊,毕竟CPU的资源也是有限的, 就升级为重量级锁就交给CPU内核进行阻塞等待,此时就意味着线程会放弃CPU,后续再由内核进行调度.

    注:线程的调度的开销是很大的,系统才不能保证多长时间内就调度到你这个线程,完全是由操作系统本身决定的,所以此时再获取到锁,再等内核调度到这个线程可能会很久的。     

   还有注意:jvm不支持降级操作,也就是锁只能进行升级,而不能进行降级。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良月初十♧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值