多线程同步-synchronized

1.线程涉及到主存和工作内存的概念:

 

  • CPU在读取运算所需的数据、存储运算得到的数据结果等操作的时候,会涉及到与内存的读写交互,但CPU运算速度远高于内存的读写速度,为了解决这种速度不匹配的情况,增加了多级高速缓存;这样,运算前,将数据复制到高速缓存,运算后,将结果同步写回主存(对于JVM来说,这个主存是指Java堆内存);
  • CPU在计算时,并不总是从主存中读数据(数据是指共享变量,存在线程间共享和竞争的关系),而是通过从多级缓存中拿数据,从而提高执行效率。读取数据的优先级是:寄存器 > 高速缓存(多级) > 主存;

  • 所以线程的工作内存是个抽象概念,指的是寄存器和(多级)高速缓存,线程的工作内存中保存的是主存中数据的备份;

  • 当多线程访问主存中的某个经常被访问的数据时,是先从线程的工作内存中读写数据,然后在某个合适的时候再写回主存中,所以每个工作线程中的副本变量不一定是最新的(对其他线程是不可见的),这就是上面多线程读写count字段出现线程不安全的原因;

所以需要加锁保证多线程间数据同步,volatile字段的作用只能保证数据的可见性和有序性,即:线程A对volatile count字段执行写操作后,会立即将count值写回到主存中,并通知其他线程count值发生变化,这样count对其他线程就是可见的了,但是volatile并不会保证操作的原子性;

2.4种使用方式

public class Foo {
    private int count = 0;
    private Object o = new Object();

    public static synchronized void t1() {
        // 静态方法加synchronized和t2()的方式争用的都是类锁,和对象锁唯一的区别就是,内存中类锁只有一把;
    }

    public void t2() {
        synchronized (Foo.class) {
        }
    }

    public synchronized void t3() {
    }

    public void t4() {
        synchronized (this) {
        }
    }

    public void t5() {
        // 如果使用下面的o对象,多线程并发访问t5(),结果是什么?
        // Object o = new Object();
        synchronized (o) {
            count++;
        }
    }
}

 3.总结

  • Java代码添加synchronized修饰符后,编译器编译成字节码时,会在进入同步代码块前添加monitorenter指令,退出代码块时添加monitorexit指令;
  • 在机器码执行模式下,monitorenter这个Java指令对应的汇编代码被执行时,执行了quick_lock_entrypoints.cc文件中的artLockObjectFromCode()函数;
  • artLockObjectFromCode()函数调用了obj->MonitorEnter(self)函数,内部调用Monitor::MonitorEnter()函数;
  • Monitor::MonitorEnter()函数根据判断,如果同一个线程获取锁,则更新lock count;如果多线程争用锁,则看情况,严重的话使用ART提供的Mutex互斥锁;
  • 而Mutex的实现是使用了系统调用futex()函数,所以在多线程争用锁的时候,最终是由Linux的系统调用futex()保证多线程同步机制的;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值