多线程之synchronized 关键字

synchronized

多线程问题

//希望输出是0,但结果几乎每次都不一样
public class IncTest {
    private volatile static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread() {
            @Override
            public void run() {
                for( int j = 0 ; j < 1000000 ; j++ ) i++;
            }
        };
        a.start();
        Thread b = new Thread() {
            @Override
            public void run() {
                for( int j = 0 ; j < 1000000 ; j++ ) i--;
            }
        };
        b.start();
        a.join();
        b.join();
        System.out.println(i);
    }
}

其中结果可能不为0的原因就是其中的i++ 和 i-- 不是 原子性 的操作
即在线程a操作i变量值的时候可能会有另外的线程插入进来也进行i变量的操作,
从而导致i变量 值变化的不稳定性

java多线程解决方法

解决多线程问题的方式就在于将并发变成 同步,即操作共享变量的原子性
同一时刻 内一般只能由 一个线程进行操作(共享锁除外)
java保证共享变量操作的原子性方法有:

  • synchronized 锁机制
  • 循环 CAS

syn用法

syn用法用法含义
语句级别synchronized (Object obj) {同步代码块}obj对象 中持有锁,谁拥有obj对象中的锁即可执行同步代码块,否则需等待其他线程释放obj中的锁
实例方法级别public synchronized void methodName(paramters…){方法块}等同于 锁为 实例对象this : public void methodName(paramters…){synchronized (this){方法块} }
静态方法级别public static synchronized void methodName(paramters…){方法块}等同于 锁为 类对象class : public void methodName(paramters…){synchronized (本类Class对象)){方法块} }

synchronized过程

锁对象头的内存结构

大小名称存储内容
64bitMark Word对象 hashcode,gc 分代年龄,锁相关信息(锁的类型,锁对应的线程ID,锁的状态(是否被获取))
64bitClass Metadata Address对象对应的类型地址
32bitArray Length如果是数组对象,即为数组长度

syn 锁的升级

偏向锁

适用场景 : 适用于竞争较少,经常处于同一个线程自己竞争锁和释放锁的情况,就好比于有一个公共卫生间,
但是一般只有一个人在那片区域使用厕所,可能就上厕所不需要关门,也不需要调整厕所指示灯,全部显示为占用即可,
这样就减少了指示灯调整的开销。


竞争锁

成功
失败
线程A执行同步代码块
竞争锁,查看markword是否为偏向锁类型
查看markword是否被锁住状态
CAS替换锁状态,将锁指向线程id改为线程A
成功拥有偏向锁
有其他线程竞争,偏向锁升级成轻量锁,线程A开始自旋CAS获取锁
markword中的线程id是否为线程A
成功获取偏向锁,本身就已经拥有偏向锁
CAS修改markword线程id,暂停线程B查看是否在执行同步代码块
CAS修改成功,markword指向线程A,线程A成功获取偏向锁
CAS修改失败,偏向锁升级为轻量锁,线程A开始自旋获取轻量锁

理想状态下的偏向锁竞争

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lp2rLxhD-1575521748542)(en-resource://database/522:0)]

轻量锁

使用场景: 适用于竞争一般,读多写少的情况
简介: 在CAS 竞争锁失败后,便自旋CAS 获取锁

重量锁

简介:
获取锁 CAS获取锁失败后,自身阻塞,等待持有锁的线程释放锁通知后才继续CAS争取锁
释放锁 释放锁,通知其他阻塞线程开始竞争锁
适用场景:
适用于竞争激烈,写比较多的情况

各种锁的比较
优点缺点适用场景
偏向锁对于同个线程的锁切换几乎没有开销对于多个线程竞争锁需要临时暂停持有锁线程,导致竞争锁时延时加大锁竞争不是特别激烈,适用于只有一个线程访问情景
轻量锁自旋CAS,响应时间较短如果长时间没有获取到锁,cpu消耗较大读多写少,追求响应时间
重量锁通知机制实现,不会过度消耗cpu,增大了吞吐量线程阻塞,响应时间较慢竞争较大,追求吞吐量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值