线程同步与synchronized锁

Java并发编程:Lock - Matrix海子 - 博客园

二.如何解决线程安全问题?

当多个线程同时对同一共享资源进行操作时,就会产生线程安全问题。为了保证线程安全,经常会使用synchronized和Lock锁。

 采用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问。 

三.synchronized同步方法或者同步块

Synchronized的语义底层是通过一个monitor的对象来完成:Java中每个对象都有一把monitor锁。一个要执行对象的synchronized代码的线程必须先获得那个对象的锁。

四、Synchronized的基本使用

       (1)修饰普通方法

  (2)修饰静态方法

  (3)修饰代码块

五、java对象
 

对齐填充:对象的起始地址必须为8字节的整数倍

六、Synchronized 原理

Synchronized代码块进行反编译:

参考JVM规范中描述:

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,

monitorenter :

线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2、如果该线程之前已经占有monitor,只是重新进入,则进入monitor的进入数加1.

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

monitorexit: 

执行monitorexit的线程必须是object所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 

同步方法的反编译结果:

源代码:

1 package com.paddx.test.concurrent;
2 
3 public class SynchronizedMethod {
4     public synchronized void method() {
5         System.out.println("Hello World!");
6     }
7 }

反编译结果:

JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

七、为什么优化synchronized 


JDK1.6以后对sychronized进行了锁优化,在并发量不太大的情况下不用讲线程挂起,减少了线程挂起和唤醒这个上下文切换过程的过程开销。因为挂起和唤醒线程需要用户态到内核态的切换,切换时间长成本高。


无锁:不加synchronized关键字,表示无锁

偏向锁:将线程id记录到对象的markword中。比较Markword的线程id是否和当前线程id相等比较后不相等说明有其他线程竞争锁,synchronized会升级成轻量级锁

轻量级锁: 当存在第二个线程申请同一个锁对象时,偏向锁就会立即升级为轻量级锁。多个线程交替进入临界区

自旋锁:

重量级锁: 多个线程同时进入临界区,有两种情况会膨胀成重量级锁。1种情况是cas自旋10次还没获取锁。第2种情况其他线程正在cas获取锁,第三个线程竞争获取锁,锁也会膨胀变成重量级锁。

synchronized的执行过程: 
1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁 
2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1 
3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。 
4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁 
5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。 
6. 如果自旋成功则依然处于轻量级状态。 
7. 如果自旋失败,则升级为重量级锁。没有获取到锁的线程阻塞挂起,直到持有锁的线程执行完同步块唤醒他们;

八、synchronized的特性:

原子性: 同一时间只有一个线程能拿到锁,进入代码块

注意!面试时经常会问比较synchronized和volatile,它们俩特性上最大的区别就在于原子性,volatile不具备原子性。

可见性:一个线程对变量的修改,其他线程获取锁之后可见。synchronized关键字包含了两个JVM指令:monitor enter和monitor exit,它能够保证在任何时候任何线程执行到monitor enter时都必须从主内存中获取数据,而不是从线程工作内存获取数据,在monitor exit之后,工作内存被更新后的值必须存入主内存,从而保证了数据可见性。

而volatile的实现类似,被volatile修饰的变量,每当值需要修改时都会立即更新主存,主存是共享的,所有线程可见,所以确保了其他线程读取到的变量永远是最新值,保证可见性。

可重入性:线程请求自己已持有的锁对象

不可中断:一个线程获取锁之后,只要他不释放,其他线程只能等待

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值