JAVA锁概念总结

锁的概念

锁是为了保证数据一致性,必须采取的手段
锁的分类为
1.乐观锁,悲观锁{
乐观锁:认为一个线程去拿数据的时候不会有其他线程对数据进行更改,所以不会上锁
实现方式:CAS机制、版本号机制
悲观锁:悲观锁认为一个线程去拿数据时一定会有其他线程对数据进行更改。所以一个线程在拿数据的时候都会顺便加锁,这样别的线程此时想拿这个数据就会阻塞。比如Java里面的synchronized关键字的实现就是悲观锁。
实现方式:加锁。
}
2.独享锁,共享锁{
synchronized是独享锁;
可重入锁ReentrantLock是独享锁;
读写锁ReentrantReadWriteLock中的读锁ReadLock是共享锁,写锁WriteLock是独享锁。
}
3.公平锁,非公平锁
4.互斥锁,读写锁
5.可重入锁
6.分段锁
7.锁升级(锁的状态){

1.无锁--->偏向锁(严格来讲他并不是锁)--->自旋锁(也叫轻量级锁,通过不断的循环等待,相当于CPU不断的空转,消耗性能所以出现了适应性自旋CAS)--->重量锁(需要进入队列,需要调度)
锁只可以升级,不可以降级
}

synchronized

在jdk早期的时候synchronized是重量级锁,很消耗性能,随着版本的优化synchronized有一个锁升级的过程偏向锁->自旋锁(轻量级锁)->重量锁
含义:在只有一个线程抢占资源的时候,是偏向锁,偏向锁不需要竞争,直接贴一个id就能访问锁,没有锁竞争的过程,但是当另外一个资源来抢占的时候,需要竞争,那么他会自动的升级锁

常用的几种synchronize方法,获得当前锁的对象

public synchronized void method()

下面这种方法等价于上面

public void method(){
    synchronized(this)
}

还可以给static函数

public synchronized static void method(){}

当用你static的时候 指向的是获取Class这个类的锁
在这里插入图片描述

虽然synchronized可以保证多线程的安全性,但是不足以控制复杂的逻辑,需要Object对象的wait()方法和notify()方法进行控制
函数wait()可以让线程等待当前对象的通知,收到notify()通知的时候,wailt()过程中的线程释放,如果有多个线程等待,那么notify()会随机选择一个

public class BlockQueue{
    private List lis =  new ArrayList()
    
    public synchronized Object pop() throws UbterruptedException{
        while(list.size <=0){
            this.wait() // 队列为空等待
        }
        if(list.size()>0){
            return list.remove(0);
        }
        return list;
    }
    
    public synchronized void put(Object obj){
        list.add(0);
        this.notfit(); // 唤醒被wait等待的线程
    }
}

Object还提供了wait(long timeout):最大等待时间不超过多小毫秒
void wait(long timeout,int nanos):最大等待不超过多少秒和多少毫秒
notifyAll:方法唤醒所有的等待对象

ReentrantLock重入锁

ReentrantLock重入锁,他比synchronized拥有强大的功能,他可以中断,可以定时,JDK5中 在高并发情况下比synchronized性能更好,但是在JDK6中,由于JVM的优化,两者差别不是很大

同时ReentrantLock 提供了公平锁和非公平锁两种锁
公平锁:可以保证在锁的等待队列中的各个线程是公平的,锁的获取总是现进先出
非公平锁:可能会存在插队的情况
通过以下函数可以指定是否公平

public ReentrantLock(boolean fair)

使用ReentrantLock必须释放锁,一般写在finally里,否则如果程序出现异常,永远无法释放锁
相比于synchronized ,JVM虚拟机总会在最后释放synchronized锁

ReentrantLock有如下方法

lock()获得锁
lockInterruptibly() 获得锁,优先响应中断
tryLock() 获得锁,如果成功返回true 失败false 方法不等待,立即返回
tryLock(long time) 给定时间内尝试获得锁
unlock() 释放锁

ReadWriteLock读写锁

如果线程A1 A2 A3 进行写操作 B1 B2 B3 进行读操作,如果使用重入锁或者内部锁,这理论上都是读和写串行的,当B1 读 的时候,B2 B3需要进行等待,由于读操作对数据的完整性不会造成破坏,这种等待不合理。
读写锁允许多个线程同时读,使得B1 B2 B3实现并行,但是考虑到数据的完整性,写写操作和读读操作他们之间依然需要相互等待,如果在一个系统中,读操作多余写操作,那么则可以用读写锁。
定义重入锁和读写锁

定义重入锁
private static Lock lock  = new ReentrantLock();
定义读写锁
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReedWriteLock();
private static Lock readLock = reentrantReadWriteLock.readLock();
private static Lock wirtLock = reentrantReadWriteLock.writeLock();

CAS

如果原始值是0 读取0–A 修改值为1—B 然后判断读取的值(A操作)和修改的值(B操作)是否相等 ,如果在没有其他线程修改操作A的情况下,原先操作A和 修改完后得到B的结果的操作A的值相等 如果相等,那么覆盖掉,如果有其他操作修改了值,在进行操作B修改的时候,有操作C修改了操作A的值为8,那么操作B时候的操作A的值为0和被操作C修改存入的值为8的操作C对比 0!=8 然后就会重新读取操作C,然后将读取的操作C的值进行操作B的操作(重复循环)
在这里插入图片描述
著名的CAS的操作的问题
ABA问题
描述:在进行操作B的时候,操作A的值本来是自身为0,但是别的线程修改了自身的0,改为不是自身的0的值,虽然值是0,但是不是原来的0了。
解决:加版版本号(加boolean类型的和数字类型的时间戳)

以上参考<JAVA程序性能优化>以及B站的马士兵老师的课程等整理而得的笔记。希望对于自己的提升有所帮助,知不足,求上进,如果文中有错误欢迎各位留言指出,一同讨论进步。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值