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会用到的东西,一个unsafe类,一个AQS
unsafe类 ,不安全的操作,直接访问系统内存资源
先用反射获得,它会避免用户使用
做对内存的操作,cas操作,线程调度,这个unsafe类反正有点吊,可以阻塞线程,lock就会用到。
Lock基于AQS
AQS也只是个框架
构建锁和同步器的框架,底层用了cas技术保证
AQS原理分析
AQS底层原理,和sys底层原理一样。
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删除掉。