前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java并发技术总结之六——Java锁分类

Java并发技术总结之六——Java锁分类

作者头像
剑影啸清寒
发布2020-07-15 15:56:19
3440
发布2020-07-15 15:56:19
举报
文章被收录于专栏:琦小虾的Binary琦小虾的Binary

六. Java 锁分类

《Java并发编程:Lock》 《java 锁 Lock接口详解》

《[死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁]》)

6.1 Java 锁的分类

锁的类型目前感觉可以分成两大类:synchronized 关键字,以及 Lock, ReadWriteLock 锁以及 Reentrant 为前缀修饰的实现类 (ReentrantLock, ReentrantReadWriteLock);

其他角度来看,按照不同分类类型的锁:

  • 实现方式:synchronized / Lock, ReadWriteLock 及其实现类;
  • 可中断性:synchronized / Lock
  • 公平性:ReentrantLock 构造函数中,传入 boolean 值,可以控制公平性,默认非公平;公平锁按照锁的申请顺序分配锁,申请锁时间最长的线程在下一次会最早得到锁;非公平锁不对申请锁的时间进行保证,所以可能导致某个线程永远都获取不到锁;
  • 可重入性:synchronized / ReentrantLock,如果一个线程已经获得了一个对象锁,此后该线程再请求进入被该对象锁的同步代码块时,由于该线程之前已经获取了这个对象锁,所以可以直接进入该锁的同步代码块;
  • 读写性:ReadWriteLock;

可重入性的原理:参考《深入理解 Java 虚拟机》P391 可重入锁和不可重入:参考《Java不可重入锁和可重入锁理解》 对于一个对象,进入对象锁的代码域之后,线程对该锁进行计数。没有进入锁时,该锁的计数值为 0。第一个获取到该锁的线程获得该对象锁,此后每多一个线程对该锁进行申请,则计数值 +1。每当有一个线程释放了这个锁,则该锁对应的计数值 -1。直到这个锁的计数值重新回到 0,其他线程才可以获取到该锁的所有权。 我对于对象锁的理解:

  • 对于 synchronized,可以为 synchronized 方法的实例对象 this,或者 synchronized(object) 的 object,都是对象锁;
  • 对于 Lock,就是 lock 对象本身;

不可重入锁:以可重入的对立面理解即可:对于某个 Lock 的实现,如果该 Lock 被线程A锁住,线程A的其他对象想要获取该 Lock,但由于在此之前 Lock 已经被锁住,所以这里不能获取到该 Lock。总之感觉不可重入锁并不是一种合适的 Lock 的类型。

不可重入锁的代码实例如下:

代码语言:javascript
复制
public class Lock {
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException {
        while(isLocked) {    
            wait();
        }
        isLocked = true;
    }
    public synchronized void unlock() {
        isLocked = false;
        notify();
    }
}

public class Count {
    Lock lock = new Lock();
    public void print() {
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd() {
        lock.lock();
        //do something
        lock.unlock();
    }
}

Lock 接口主要有六个方法:

代码语言:javascript
复制
// 获取锁
void lock();
// 获取锁,可中断
void lockInterruptibly() throws InterruptedException;
// 尝试获取锁;如果没有获取到,则返回 false;如果获取到则返回 true
boolean tryLock();
// 尝试在某段时间内获取锁,如果等待一段时间仍没有获取到则返回 false;
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 释放锁
void unlock();
// 条件锁
Condition newCondition();

6.2 公平锁与非公平锁

为什么ReentrantLock默认采用的是非公平模式?因为非公平模式效率比较高。非公平模式会在一开始就尝试两次获取锁,如果当时正好 state 的值为 0,它就会成功获取到锁,少了排队导致的阻塞/唤醒过程,并且减少了线程频繁的切换带来的性能损耗。

同时非公平模式也存在弊端。非公平模式有可能会导致一开始排队的线程一直获取不到锁,导致线程饿死。

公平锁与非公平锁在源码上的区别:

  • 公平锁:在尝试获取锁的时候,首先会判断 AQS 线程队列的头部是否为当前线程(队列的特性决定了公平性),如果当前线程位于 AQS 线程队列的头部,则说明当前线程等待的时间最长,当前线程有资格比较状态,然后才能获取到锁。
  • 非公平锁:尝试获取锁的时候不会判断 AQS 队列头部信息,直接进行比较状态并尝试获取锁。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-07-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 六. Java 锁分类
    • 6.1 Java 锁的分类
      • 6.2 公平锁与非公平锁
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档