Java显式锁详情

目录
  • Java显式锁
    • 一、显式锁
    • 二、Lock的常用api
    • 三、Lock的标准用法
    • 四、ReentrantLock(可重入锁)
      • 1. 锁的可重入性
      • 2. 公平锁和非公平锁
    • 五、ReentrantReadWriteLock(读写锁)
    • 六、Condition

Java显式锁

一、显式锁

什么是显式锁?

由自己手动获取锁,然后手动释放的锁。

有了 synchronized(内置锁) 为什么还要 Lock(显示锁)?

使用 synchronized 关键字实现了锁功能的,使用 synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。

与内置加锁机制不同的是,Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。

二、Lock的常用api

方法名称 描述
void lock() 获取锁
void lockInterruptibly() throws InterruptedException 可中断的获取锁,和lock()方法的不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程
boolean tryLock() 尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException 超时获取锁,当前线程会在以下三种情况下会返回:
1. 当前线程在超时时间内获得了锁
2.当前线程在超市时间内被中断
3. 超时时间结束,返回false
void unlock(); 释放锁

三、Lock的标准用法

lock.lock();
try {
    // 业务逻辑
} finally {
    lock.unlock();
}
  • finally 块中释放锁,目的是保证在获取到锁之后,最终能够被释放。
  • 不要将获取锁的过程写在 try 块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。

四、ReentrantLock(可重入锁)

Lock接口常用的实现类是 ReentrantLock。

示例代码:主线程100000次减,子线程10万次加。

public class ReentrantLockTest {

    private Lock lock = new ReentrantLock();
    private int count = 0;

    public int getCount() {
        return count;
    }

    private static class ReentrantLockThread extends Thread {
        private ReentrantLockTest reentrantLockTest;

        public ReentrantLockThread(ReentrantLockTest reentrantLockTest) {
            this.reentrantLockTest = reentrantLockTest;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                reentrantLockTest.incr();
            }
            System.out.println(Thread.currentThread().getName() + " end, count =  " + reentrantLockTest.getCount());
        }
    }

    private void incr() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    private void decr() {
        lock.lock();
        try {
            count--;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
        new ReentrantLockThread(reentrantLockTest).start();

        for (int i = 0; i < 100000; i++) {
            // 递减100000
            reentrantLockTest.decr();
        }
        System.out.println(Thread.currentThread().getName() + " count =  " + reentrantLockTest.getCount());
    }
}

1. 锁的可重入性

简单地讲就是:“同一个线程对于已经获得到的锁,可以多次继续申请到该锁的使用权”。而 synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁

同样,ReentrantLock 在调用 lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞

2. 公平锁和非公平锁

  • 如果在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说锁获取是顺序的。
  • ReentrantLock 提供了一个构造函数,能够控制锁是否是公平的(缺省为不公平锁)。事实上,公平的锁机制往往没有非公平的效率高。
  • 在激烈竞争的情况下,非公平锁的性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。
  • 假设线程 A 持有一个锁,并且线程 B 请求这个锁,由于这个锁已被线程 A 持有,因此 B 将被挂起。当 A 释放锁时,B 将被唤醒,因此会再次尝试获取锁。与此同时,如果 C 也请求这个锁,那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁,这样的情况是一种“双赢”的局面:B 获得锁的时刻并没有推迟,C 更早地获得了锁,完成了自己的任务,然后释放了锁,并且吞吐量也获得了提高。

五、ReentrantReadWriteLock(读写锁)

ReentrantReadWriteLock 是 ReadWriteLock 的实现类。

之前提到锁基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。

读锁不排斥读锁,但是排斥写锁;写锁即排斥读锁也排斥写锁。

private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock getLock = lock.readLock(); // 读锁
private final Lock setLock = lock.writeLock(); // 写锁

至于上锁、解锁与 ReentrantLock 使用方式一致。

六、Condition

  • 任意一个 Java 对象,都拥有一组监视器方法(定义在 java.lang.Object 上),主要包括 wait()、wait(long timeout)、notify()以及 notifyAll()方法,这些方法与 synchronized 同步关键字配合,可以实现等待/通知模式。
  • Condition 接口也提供了类似 Object 的监视器方法,与 Lock 配合可以实现等待/通知模式。

常用api

方法名称 描述
void await() throws InterruptedException 使当前线程进入等待状态直到被通知(signal)或中断
void signal() 唤醒一个等待的线程
void signalAll() 唤醒所有等待的线程

示例代码,主线程调用方法唤醒两个子线程。

public class ConditionTest {

    private volatile boolean flag = false;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private void task1() {
        lock.lock();
        try {
            try {
                System.out.println(Thread.currentThread().getName() + " 等待中");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 等待结束");
            System.out.println("发送邮件");
        } finally {
            lock.unlock();
        }
    }

    private void task2() {
        lock.lock();
        try {
            try {
                System.out.println(Thread.currentThread().getName() + " 等待中");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 等待结束");
            System.out.println("发送短信");
        } finally {
            lock.unlock();
        }
    }

    private void updateFlag() {
        lock.lock();
        try {
            this.flag = true;
            this.condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    private static class ConditionThread1 extends Thread {
        private ConditionTest conditionTest;
        public ConditionThread1(ConditionTest conditionTest) {
            this.conditionTest = conditionTest;
        }

        @Override
        public void run() {
            if (!conditionTest.flag) {
                conditionTest.task1();
            }
        }
    }

    private static class ConditionThread2 extends Thread {
        private ConditionTest conditionTest;
        public ConditionThread2(ConditionTest conditionTest) {
            this.conditionTest = conditionTest;
        }

        @Override
        public void run() {
            if (!conditionTest.flag) {
                conditionTest.task2();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionTest conditionTest = new ConditionTest();
        new ConditionThread1(conditionTest).start();
        new ConditionThread2(conditionTest).start();
        Thread.sleep(1000);
        System.out.println("flag 改变。。。");
        conditionTest.updateFlag();
    }
}

以上就是Java显式锁详情的详细内容,更多关于Java显式锁的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java中的15种锁

    目录 一.公平锁 / 非公平锁 1.公平锁 2.非公平锁 二.可重入锁 / 不可重入锁 1.可重入锁 2.不可重入锁 3.ReentrantLock中可重入锁实现 三.独享锁 / 共享锁 四.互斥锁 / 读写锁 1.互斥锁 2.读写锁 五.乐观锁 / 悲观锁 1.悲观锁 2.乐观锁 六.分段锁 七.偏向锁 / 轻量级锁 / 重量级锁 1.锁的状态 2.偏向锁 3.轻量级 4.重量级锁 八.自旋锁 1.什么是自旋锁? 2.Java如何实现自旋锁? 3.自旋锁存在的问题 4.自旋锁的优点 5.自旋锁

  • 深入理解Java显式锁的相关知识

    目录 一.显式锁 二.Lock的常用api 三.Lock的标准用法 四.ReentrantLock(可重入锁) 五.ReentrantReadWriteLock(读写锁) 六.Condition 一.显式锁 什么是显式锁? 由自己手动获取锁,然后手动释放的锁. 有了 synchronized(内置锁) 为什么还要 Lock(显示锁)? 使用 synchronized 关键字实现了锁功能的,使用 synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放.

  • Java检测死锁案例

    导致死锁的程序 package com.study.train; import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.Field; import java.util.*; import j

  • Java基础之线程锁相关知识总结

    一. synchronized关键字 1.对象锁 a.当使用对象锁的时候,注意要是相同的对象,并且当有线程正在访问对象锁内部的代码的时候,其他线程无法访问.(注意无法访问的范围). b.但是并不影响没有使用对象锁的部分的代码的运行. 对象锁分为两类一个叫做synchronized代码块(圆括号内是普通类的对象),另外一个是sybchronized修饰普通成员方法.它们二者其实可以通过this关键字进项转化. 2.类锁 a. 当使用类锁的时候,只要是同一个类的对象.当有线程正在访问类锁内部的代码的

  • 详细介绍Java中的各种锁

    一.一张图了解21种锁 二.乐观锁 应用 CAS 思想 一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改 实现 写数据时,判断当前 与期望值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的) 三.悲观锁 应用 synchronized.vector.hashtable 思想: 一种悲观思想 ** ,即认为写多读少,遇到并发写的可能性高 实现 每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁 缺点 他线程想要读写这个数据时,

  • java锁升级过程过程详解

    目录 1.说到锁升级的过程,我们就得说一下对象头 对象头 对象头的存在形式 接下来让我们看看锁升级的过程 专业版解释 我通过马士兵老师讲的带味道的栗子大致懂了这个过程(菜鸟版理解) 总结 1.说到锁升级的过程,我们就得说一下对象头 对象头 java对象保存在内存中,由3个部分组成: 1. 对象头 2. 实例数据 3. 对齐填充字节 4. 如果是数组还包含数组长度 对象头的存在形式 让我们先看看图,主要来说一下 Mark Word markword 8bytes class pointer - 指

  • 详解Java中的锁Lock和synchronized

    一.Lock接口 1.Lock接口和synchronized内置锁 a)synchronized:Java提供的内置锁机制,Java中的每个对象都可以用作一个实现同步的锁(内置锁或者监视器Monitor),线程在进入同步代码块之前需要或者这把锁,在退出同步代码块会释放锁.而synchronized这种内置锁实际上是互斥的,即没把锁最多只能由一个线程持有. b)Lock接口:Lock接口提供了与synchronized相似的同步功能,和synchronized(隐式的获取和释放锁,主要体现在线程进

  • Java多线程之多种锁和阻塞队列

    一.悲观锁和乐观锁 1.1. 乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制. 乐观锁适用于多读的应用类型,乐观锁在Java中是通过使用无锁编程来实现,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的. CAS全称 Compare And Swap(比较与交换),是一种无锁算法.在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步.java.util

  • 浅谈java安全编码指南之死锁dead lock

    不同的加锁顺序 我们来看一个不同加锁顺序的例子: public class DiffLockOrder { private int amount; public DiffLockOrder(int amount){ this.amount=amount; } public void transfer(DiffLockOrder target,int transferAmount){ synchronized (this){ synchronized (target){ if(amount< tr

  • java排查死锁示例

    目录 死锁示例 死锁产生原因 死锁排查 方案 1:jstack 方案 2:jconsole 方案 3:jvisualvm 方案 4:jmc 总结 死锁(Dead Lock)指的是两个或两个以上的运算单元(进程.线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁. 死锁示例 接下来,我们先来演示一下 Java 中最简单的死锁,我们创建两个锁和两个线程,让线程 1 先拥有锁 A,然后在 1s 后尝试获取锁 B,同时我们启动线程 2,让它先拥有锁 B,然后在 1s 之

随机推荐