Synchronized初步认识

Synchronized

概念

是利用锁的机制来实现同步的。

锁机制有如下两种特性:

互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。
可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。

Synchronized的使用方法

1、同步方法

(1) 同步非静态方法
Public synchronized void methodName(){
……
}
(2) 同步静态方法
Public synchronized static void methodName(){
……
}

2、同步代码块
1.synchronized(this/object) {}

Private final Object MUTEX =new Object();
Public void methodName(){
Synchronized( MUTEX ){
……
}
}

2.synchronized(类.class) {}

public class demo {
Public void methodName(){
Synchronized( demo .class ){
……
}
}
ClassLoad加载一个自己买(class)到堆Class对象----->Class内所有的对象都共同使用一个锁(Synchronized----->monitor—>每个对象都有这个monitor(监视器)计算器monitor为0则没有线程占有这个对象,不为0则表示被占用)

根据获取的锁分类

1、获取对象锁
synchronized(this|object) {}
修饰非静态方法
在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰
2、获取类锁
synchronized(类.class) {}
修饰静态方法
在 Java 中,针对每个类也有一个锁,可以称为**“类锁”**,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。

在 Java 中,每个对象都会有一个 monitor 对象,监视器。

  1. 某一线程占有这个对象的时候,先monitor 的计数器是不是0,如果是0还没有线程占有,这个时候线程占有这个对象,并且对这个对象的monitor+1;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1;
  2. 同一线程可以对同一对象进行多次加锁,+1,+1,重入性
重入性

是指在同一个线程在外层方法获取锁的时候,在进入该方法内层还可以给方法内的方法或者字段再次加锁,而不会被阻止。

synchronized原理分析

原理之后专门分析(在jdk包下有个jconsole,用于线程分析monitorenter和monitorexit用于代码块
在这里插入图片描述
在这里插入图片描述
flags: ACC_SYNCHRONIZED 信号量用于方法的synchronized加锁
在这里插入图片描述

Java虚拟机对synchronized的优化

在JDK1.6之前synchronize都是重量级锁(由目态进入管态),没有ReetrantLock好用,之后优化了偏向锁轻量级锁性能就比ReetrantLock好了,不在是每次使用都为重量级锁

偏向锁:在对象第一个被某一线程占有的时候,是否偏向锁置1,一般来说偏向锁偏向第一个到来的线程,会把锁给它,其它的锁竞争为失败,与无锁状态接近,时候竞争不激烈的线程

轻量级锁:线程有交替适用,互斥性不是很强,CAS失败,00

重量级锁:强互斥,10,等待时间长

前三种锁的状态是通过对象监视器在对象头中的字段来表明的。至针对Synchronized

自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环,一般5次

锁消除:JIT在编译的时候把不必要的锁去掉

独享锁:是指该锁一次只能被一个线程所持有。只有一个资源

共享锁:该锁可以被多个线程所持有。可以有多个资源分配出去,直到拥有的资源都被分配占有还未释放又有线程申请资源着新来的线程失败,且等待或者抛出异常

CAS:(Compare and Swap 比较并交换),当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

CAS操作中包含三个操作数——需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,否则处理器不做任何操作。

乐观锁:(先去拿锁)总是认为不存在并发问题,每次去取数据的时候,总认为不会有其他线程对数据进行修改,因此不会上锁。但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用“数据版本机制”或“CAS操作”来实现。

悲观锁:(先加个锁)认为对于同一个数据的并发操作,一定会发生修改的,哪怕没有修改,也会认为修改。因此对于同一份数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁并发操作一定会出问题。

在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。

如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

期间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
公平锁:是指多个线程按照申请锁的顺序来获取锁。

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
  对于Java ReetrantLock而言,通过构造函数指定该锁是否是公平锁默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。

对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并**没有任何办法使其变成公平锁。
  **
分段锁:其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值