java常见的锁的类型

java常见的锁的类型

1.公平锁与非公平锁

公平锁:每个线程在获取锁时会先查看此锁维护的等待队列,如果为空或当前线程是等待队列的第一个,就占有锁,否则就加入到等待队列中,以后会FIFO的规则来从队列获取锁ReentrantLock的创建可以指定构造函数的boolean类型来指定得到公平锁或非公平锁,如果不写默认是false-非公平锁
ReentrantLock lock = new ReentrantLock(true);
非公平锁:上来就直接尝试获取锁,如果失败就采用公平锁的模式获取。。非公平锁吞吐量比较大 。
以下两个都是非公平锁
ReentrantLock lock = new ReentrantLock(false);
synchronized

2.可重入锁(递归锁)

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在内层方法会自动获取锁
也就是:线程可以进入任何一个它已经拥有的锁所同步着的代码块
以下两个都是可重入锁
ReentrantLock
synchronized

3.独占锁(写锁)与共享锁(读锁):

独占锁(写锁) 一次只能被一个线程所持有,ReentrantLock、synchronized都是独占锁
共享锁(读锁) 该锁可以被多个线程所持有
读写锁的内部包含两把锁:一把是读(操作)锁,是一种共享锁;另一把是写(操作)锁,是一种独占锁 在没有写锁的时候,读锁可以被多个线程同时持有
写锁被一个线程持有,其他的线程不能再持有写锁,抢占写锁会阻塞,抢占读锁也会阻塞
读读相容
读写互斥
写写互斥

读写锁实例 ReentrantReadWriteLock

public class ReadWriteLock {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 0; i < 5; i++) {
            final  int temp = i;
            new Thread(()->{
                myCache.put(temp+"",temp);
            },String.valueOf(i)).start();
        }
        for (int i = 0; i < 5; i++) {
            final  int temp = i;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();
    //读写锁
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    public void put(String key,Object value){
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"\t 正在写入:"+key);
            TimeUnit.MICROSECONDS.sleep(300);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"\t 写入完成:");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rwLock.writeLock().unlock();
        }
    }
    public Object get(String key){
        rwLock.readLock().lock();
        System.out.println(Thread.currentThread().getName()+"\t 正在读取:"+key);
        try {
            TimeUnit.MICROSECONDS.sleep(300);
            Object o = map.get(key);

            System.out.println(Thread.currentThread().getName()+"\t 读取完成:"+o);
            return o;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rwLock.readLock().unlock();
        }
        return null;
    }
}

输出:

1 正在写入:1
1 写入完成:
0 正在写入:0
0 写入完成:
2 正在写入:2
2 写入完成:
3 正在写入:3
3 写入完成:
4 正在写入:4
4 写入完成:
0 正在读取:0
1 正在读取:1
2 正在读取:2
3 正在读取:3
4 正在读取:4
0 读取完成:0
3 读取完成:3
2 读取完成:2
1 读取完成:1
4 读取完成:4

4.自璇锁

获取锁的线程不会阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文的切换的消耗,缺点是循环会消耗cpu

CAS底层的自旋锁

  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

synchronized与ReentrantLock的区别

1.原始构成
synchronized是java关键字属于JVM层面。 monitorenter(底层通过monitor对象来完成,其实wait/notify等方法也依赖于monitor对象只有在同步块或方法中才能调用wait/notify等方法)
ReentrantLock是具体类(java.util.concurrent.locks.Lock)是API层面

2.使用方法
synchronized不需要手动释放锁,代码执行完系统会自动让线程释放对锁的占用 ReentrantLock则需要手动去释放锁,若没有主动释放优肯出现死锁,也就是lock()和unlock()方法需要配合try/finally语句快来使用

3.等待是否可以中断
synchronized不可中断,除非抛出异常或者正常运行完成
ReentrantLock可中断
1.设置超时时间 tryLock(long timeout,TimeUnit unit)
2.lockInterruptibly()房代码块中调用interrupt()方法可中断

4.枷锁是否公平
synchronized是非公平锁 ReentrantLock 两者都可,默认是非公平锁。构造方法传入boolean值,true是公平锁,false是非公平锁
5.锁绑定多个条件Condition
ReentrantLock 用来实现分组唤醒需要唤醒的线程,可以精确唤醒,而不是像synchronized要么随机唤醒一个要么全部唤醒

你可能感兴趣的:(java,java,开发语言,算法)