java 锁
1. 公平锁
指多个线程按照请求锁的顺序来获取锁,类似排队打饭,先来后到。
公平锁就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空或者当前时等待队列的第一个,就占有锁,否则就加入等待队列中,以后会按照FIFO(先进先出)的规则从队列中去到自己
2.非公平锁
是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请锁的线程优先获取锁。在高并发的情况下,可能会造成优先级反转或者饥饿现象
非公平锁比较粗鲁,上来先尝试占有锁,如果尝试失败,就再采取类似于公平锁的那种方式。
非公平锁的优点在于吞吐量比公平锁大
对于Synchronized而言,也是一种非公平锁
并发包中的ReentrantLock可以指定构造函数的boolean 类型来得到公平锁或者非公平锁,默认非公平锁
Lock a = new ReentrantLock(true);//公平锁
Lock b = new ReentrantLock(false);//非公平锁
Lock c = new ReentrantLock();//默认非公平锁
3. 可重入锁 (又名递归锁)
指的时同一个线程外层函数获取锁后,内层递归函数仍然能获取该锁的代码,在同一个线程的外层方法获取锁的时候,在进入内层方法或自动获取锁。也就是说 线程可以进入任何一个它已经拥有锁所同步的代码块。
如下代码,可重入锁的意思就是,当线程获取了method01的锁后,里面再访问加锁的method02时,无需再次获取method02的锁,默认有这个锁,同上来说,你有了进你家大门钥匙,那么你就可以去你家厕所。
public syncronized void method01(){
method02();
}
public syncronized void method02(){
}
下面代码执行结果:
set 的进程信息:Thread-0
get 的进程信息:Thread-0
set 的进程信息:Thread-1
get 的进程信息:Thread-1
虽然set也加锁了,但是get得到锁后,相当于也得到了set的锁
package com.bowen;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class TestReentantLock implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
get();
}
public void get(){
lock.lock();
try {
set();
System.out.println("get 的进程信息:"+Thread.currentThread().getName());
}finally{
lock.unlock();
}
}
public void set(){
lock.lock();
try {
System.out.println("set 的进程信息:"+Thread.currentThread().getName());
}finally{
lock.unlock();
}
}
}
public class TestLock {
public static void main(String[] args) {
TestReentantLock a = new TestReentantLock();
Thread t1 = new Thread(a);
Thread t2 = new Thread(a);
t1.start();
t2.start();
}
}
4. 自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采取循环的方式去尝试获取锁。这样好处时减少线程上下文切换的消耗,缺点是会循环消耗CPU .
典型例子:unsafe.getAndAddInt方法
public final int getAndAddInt(Object var1, long var2, int var4){
int var5;
do{
var5 = this.getIntVolatile(var1, var2);
}while(!this.compareAndSwapInt(var1, var2, var5+var4));
return var5;
}
手写自旋锁
//自旋锁
class TestSpinLock{
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() +"come in lock~!");
while(!atomicReference.compareAndSet(null, thread)){
}
}
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() +"come in unlock~!");
atomicReference.compareAndSet(thread, null);
}
}
public class TestLock {
public static void main(String[] args) {
// TestReentantLock a = new TestReentantLock();
// Thread t1 = new Thread(a);
// Thread t2 = new Thread(a);
// t1.start();
// t2.start();
TestSpinLock testSpinLock = new TestSpinLock();
new Thread(()->{
testSpinLock.myLock();
try {
TimeUnit.SECONDS.sleep(5);
}catch (InterruptedException e){}
testSpinLock.myUnLock();
},"AA").start();
new Thread(()->{
testSpinLock.myLock();
try {
TimeUnit.SECONDS.sleep(5);
}catch (InterruptedException e){}
testSpinLock.myUnLock();
},"BB").start();
}
}
//结果
AAcome in lock~!
BBcome in lock~!
AAcome in unlock~!
BBcome in unlock~!
5. 独占锁(写锁)
指该锁一次只能被一个线程持有。对ReentrantLock和ASynchronized 是独占锁
6. 共享锁(读锁)
指该锁可被多个线程所持有,对ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁,
读的共享锁可以保证并发读是非常高效的,读写,写读,写写的过程互斥