线程安全(有点乱哈)

深入学习锁--Lock各种使用方法_lock.lock-CSDN博客

这篇文章也很好

1.多个进程访问共享资源,通过上锁保证数据安全

1.2锁的宏观分类方式是悲观锁和乐观锁

1.3悲观锁和乐观锁

悲观锁:拿数据上锁‘

举例:synchronzied

乐观锁:每次拿数据的时候不会上锁,更新数据,比较下版本号’cas(一种缩写,一个方法,好像叫做compareandswap)

cas:compare and swap 即比较并替换

1.4锁相关的概念

2.synchronzied

悲观锁,一种同步锁

修饰代码块

修饰方法

修饰一个静态的方法   锁标识对象就是当前类字节码对象

this锁表示对象,多个线程要抢同一把锁,那么锁标识必须是同一个。

除了静态方法锁的是字节码对象,其余都是锁的this,锁标识对象。

2.1sysconnized原理

同步方法:vm使用ACC_SYNCHRONZIED标识实现,有标识,尝试获取monitor,然后执行方法,执行结束释放montor锁

同步代码块:monitoreter和monitoreit实现同步,monitoreter会获取对应的monitor

moniter有一个记录次数计数器,

退出,再自减,减到0别的线程就可以获取、

自己再获得+1

sys锁机制,1.5以前,通过cas指令,默认sys是重量级锁,1.6以后,对锁进行了升级,sys锁优化。

用户态:操作系统提供的一些api

内核态:cpu指令

同步方法和同步代码块过monitoreter实现

每个monitor都有一个对象

每一个锁标识对象都在堆里面有一个对象

对象头里有一个mark word 标识是轻量级锁还是重量级锁,指向结构体。

通过cas机制,判断_owner,为空,owner就是当前线程

count就是重复获取的次数,再次获取同一个线程

waitset 处于wait线程队列

entrylist:阻塞等待的线程

从entrylist中获取锁,获取到锁,owner变为抢到线程的id

释放锁:等着count减为0,释放锁,当count为0则将owner设置为null

2.2   1.6 以后锁的优化

锁清除:锁消除即删除不必要的加锁操作。根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁。

锁粗化

把锁扩大,不用每个都加,将连续的锁扩大为更大的锁。

锁的升级和降级

偏向锁:使cas记录获取它的线程,一个线程获取时,避免用户态转内核态

轻量级锁:是由偏向锁升级来的,第二个没有抢到就自旋等待(10ms以后尝试获取锁),某线程长时间等待,浪费资源。

重量级锁:锁竞争情况严重,由轻量级锁升级为重量级,某线程达到最大自旋次数10次,轻量级升级为重量级锁。

大部分都是在偏向锁和轻量级锁

3.线程并发库

线程并发库:比如显示锁,lock锁,juc简称,是专门java并发设计包

显示锁

需要自己显示的加锁

原子变量类(乐观锁)

为了实现原子性操作提供的原子类

线程池相关

并发容器类:concurentmap

同步工具类:倒计时门栓,countdownLatch,有个信号量

4.线程同步 lock

lock和sys的区别

它们都是悲观锁,都是jvm级别

lock是java代码实现的,api是显示锁,sys是jvm管理的,就是隐形锁

lock是显示锁,sys是内置锁

lock是一个接口,sys是一个关键字。

lock可公平可非公平

1.6之前,lock效率高于sys,1.6就差不多了。

sys可以修饰代码块,可以修饰方法。lock只能修饰代码块。

5.lock原理

我们点进lock源码中,发现他们都继承于sync,然后我们再看sync

然后我们发现他继承于AQS(AbstactQuenedSy啥),所以他用到了AQS框架

先介绍,lock会用到的东西,一个unsafe类,一个AQS

unsafe类 ,不安全的操作,直接访问系统内存资源

先用反射获得,它会避免用户使用

做对内存的操作,cas操作,线程调度,这个unsafe类反正有点吊,可以阻塞线程,lock就会用到。

Lock基于AQS 

AQS也只是个框架

构建锁和同步器的框架,底层用了cas技术保证

AQS原理分析

AQS底层原理,和sys底层原理一样。

比如说这个方法,完全sys思想一模一样,查看当前线程,如果状态值==0(也就是monitor的owner=0),那么我们就把线程状态设置为当前线程id。如果不是,判断是不是当前线程,(也就是sys的count++的判断),是不是可重入锁,是那么count++。

再比如这个释放线程,也和sys的原理思想一样,把count减为0,同时把线程状态设置为null

所以说,为什么sys和lock思想都是一样的,不过一个是基于jvm,一个是基于代码

state(0表示没有占用,可以占用)   cas,尝试0设置为1,,失败就排列的等待。用park方法,放入队列,释放锁(和sys差不多),然后抢锁。

调用lock加锁的时候,用到了AQS,公平锁和非公平锁的方法

公平锁:比较尝试获取,cas了一下把计算器改为一(如果计算器等于0),成功把这个锁的当前设置为当前线程。失败,将计数器从0设置为1失败,当前持有锁的线程==当前线程,将计数器加1,如果等于,则是返回失败。、,阻塞然后放入等待队列。

释放线程:就是sys释放中大概意思,不过是用代码实现了

lock原理其实和sys原理差不多,不过sys是底层实现,lock代码实现,思路都可以说是一模一样,没有区别。

lock原理基于

7.线程安全乐观锁

是不加锁的,只是在修改的时候先做判断,有没有别人修改,atomic开头就是基于cas的乐观锁。

利用原子类

数据库通过version控制

工具类

countdownlatch(5) ,的值不为,则会一直阻塞,自己再新建个线程逻辑中减一。在分布式锁zookeeper中有用到,临时有序,每个监听前一个删除事件,删除了,就把它门栓设置为0。

信号量:多个线程竞争特定个数资源,做秒杀,限流(信号量限流)

8.TreadLocal,关于contoller变量对多并发的,太累了,就不写了。,其实还是加锁,不过这个效率高点。

它有一个map,每个线程都有一个map,

set值的话,默认是以当前作为key存入它底层的map的

存在一个内存泄漏的问题

entry是一个弱引用,有四种引用类型,强引用,软引用,和虚引用,关联到垃圾回收机制。

强引用:最常见的引用类型,只要存在,就不会被回收

软引用:内存不足时回收

弱引用:下次垃圾回收之前

虚引用:

强引入用在tl,map都被回收了,但是强引用没有被回收,这时导致内存泄漏,value成为一个永远无法被访问,无法被回收的对象。

treadloacl。声明为static final,还有remove删除掉。

  • 17
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值