java并发编程的各种锁

1.乐观锁

乐观锁并不是一种真正的锁,基于cas的思想,认为,并发操作不会修改数据的值。

适用于读比较多的场景(不阻塞线程,大大提效率)

2.悲观锁

悲观锁认为并发操作会对数据进行修改,认为并发操作不加实际的锁会出现错误,

适用于写比较多的操作

3.公平锁

在程序在并发操作时,一个加锁的线程工作,其他的线程会进入阻塞队列,当加锁的线

程运行结束阻塞队列的下一个线程获得cpu。(优先给排队时间最长的线程)

4非公平锁

非公平锁,当一个加锁线程在运行时,其他线程被阻塞,此线程运行结束后,其他所有线

程抢占cpu,抢到的获得cpu的运行权。

ReentrantLock 默认是非公平锁,但是底层可以通过 AQS 的来实现线程调度,所 以可以使其变成公平锁

1.AQS

当一个线程运行时,其他线程被阻塞在CLH同步队列(FIFO双向队列)里,AQS依靠让

CLH队列里有state属性,线程更改它获得锁对象

5.可重用锁

可重用锁为递归锁,当一个加锁的方法在运行,方法里又有另一个加锁的方法,那么这时

线程不会死锁,会运行里层的加锁方法。

例如;

package bingfa.writein;

/**
 * @program: mianshi
 * @ClassName Demo
 * @description:
 * @author: Mr.Yuan
 * @create: 2021-07-21 16:39
 **/

public class Demo {
    public synchronized void setA(){
        System.out.println("setA()开始");
        setB();

        System.out.println("setA()结束");
    }

    private synchronized void setB() {
        System.out.println("setB()在运行");
    }
}

测试类

package bingfa.writein;

/**
 * @program: mianshi
 * @ClassName Test
 * @description:
 * @author: Mr.Yuan
 * @create: 2021-07-21 16:41
 **/

public class Test {
    public static void main(String[] args) {
        Demo demo=new Demo();
        demo.setA();
    }
}

6.读写锁

ReadWriteLock

当线程在进行读操作的时候加读操作的锁

进行写操作的时候加写操作的锁

例如

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OrMr30sI-1626864566556)(C:\Users\57156\AppData\Roaming\Typora\typora-user-images\image-20210721163225470.png)]

特点

1.可以多个读者同时读

2.同一时间,只有一个写者可以运行

3.一旦有写者后续读者全部等待(唤醒时优先考虑写者)

7.分段锁

分段锁并非一种实际的锁,而是一种思想,用于将数据分段,并在每个分段上都会 单独加锁,把锁

进一步细粒度化,以提高并发效率

8.自旋锁

自旋锁:自旋锁是一种状态,并不是实际的锁,线程枪锁,枪锁失败后重试,抢到了就继续

抢不到就阻塞

特点:不阻塞线程提高效率,但过分消耗cpu

9.共享锁

可以被多个线程共同持有,并发进行访问。

例如:读写锁的读锁

10.独占锁

只可以被一个线程所访问,其他线程都会进入阻塞状态。

例如:synchronized,lock,ReentrantLock

11.4种锁状态

1.无状态锁

无状态所:没有锁

2.偏向锁:

偏向锁:只有一个线程访问锁对象时,会变成偏向锁,偏向锁会记住线程的id

下一次线程再次访问锁对象时,就会直接获取,提高效率(没有获取锁过程)。

3.轻量级锁

轻量级锁:当另一个线程访问锁对象,锁对象的状态此时为偏向锁,这个线程id

与偏向锁里存的线程id不同,此时锁对象的状态升为轻量级锁,线程通过不断自旋

的方式获取锁对象。(没有阻塞,效率相对较高,cpu耗费)

4.重量级锁状态

重量级锁:当锁对象的状态为轻量级锁,并发量增大,线程通过不断自旋获取锁对象

自旋超过一定次数却不能获得锁对象,这时锁对象的状态会变成重量级锁状态,让其他

没有获得锁对象的线程全部阻塞(减小cpu消耗)

12.Synchronized和ReentrantLock底层实现

1.Synchronized 是 由 JVM 实 现 的 一 种 实 现 互 斥 同 步 的 一 种 方 式,用Synchronized

修饰的代码块,在编译前前后加上指令monitorenter 和 monitorexit 两个字节码指令。

当运行到monitorenter ,如果对象没有锁,获取对象的锁,将锁的计数器加一

当运行到monitorexit时,如果对象有锁,则被阻塞。

代码演示

public class DieLock extends  Thread{

 static Object objA = new Object();

public DieLock(boolean flag) {
    this.flag = flag;
}

@Override
public void run() {
   
        synchronized (objA){
            System.out.println("if objA");
        }
   
}

}

需要一个唯一的一个对象,用这个对象的对象头,来存状态

2.ReentrantLock

ReentrantLock 主要利用 CAS+AQS 队列来实现。它支持公平锁和非公平锁,

如果有三个线程,同时获取锁,如果一个线程获取到了,另外两个线程的状态就会

失败状态。

如果使用AQS则为公平锁

那么它会将获取失败的线程存入同步队列里,锁释放后,按出队顺序来获取锁

如果不使用公平锁

那么其他的线程阻塞,等锁释放后,各个线程继续获取锁(抢占)