博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA中ReentrantLock详解
阅读量:6403 次
发布时间:2019-06-23

本文共 5773 字,大约阅读时间需要 19 分钟。

前言:本文解决的问题

  • RentrantLock与Synchronized区别
  • ReentrantLock特征
  • ReentrantLock类的方法介绍

1.什么是ReentrantLock

1.1ReentrantLock 与Synchronized区别

在面试中询问ReentrantLock与Synchronized区别时,一般回答都是

ReentrantLock

  • ReentrantLock是JDK方法,需要手动声明上锁和释放锁,因此语法相对复杂些;如果忘记释放锁容易导致死锁
  • ReentrantLock具有更好的细粒度,可以在ReentrantLock里面设置内部Condititon类,可以实现分组唤醒需要唤醒的线程
  • RenentrantLock能实现公平锁

Synchronized

  • Synchoronized语法上简洁方便
  • Synchoronized是JVM方法,由编辑器保证枷锁和释放

1.2ReentrantLock特征介绍

JAVA的java.util.concurrent框架中提供了ReentrantLock类(于JAVA SE 5.0时引入),ReentrantLock实现了lock接口,具体在JDK中的定义如下:

public class ReentrantLock implements Lock, java.io.Serializable { public ReentrantLock() {        sync = new NonfairSync();    }    /**     * Creates an instance of {@code ReentrantLock} with the     * given fairness policy.     *     * @param fair {@code true} if this lock should use a fair ordering policy     */    public ReentrantLock(boolean fair) {        sync = fair ? new FairSync() : new NonfairSync();    }}

看到一个类首先就需要知道它的构造方法有哪些,ReentrantLock有两个构造方法,一个是无参的 ReentrantLock() ;另一个含有布尔参数public ReentrantLock(boolean fair)。后面一个构造函数说明ReentrantLock可以新建公平锁;而Synchronized只能建立非公平锁

那么Lock接口有哪些方法

lock
Lock接口中有lock和unlock方法,还有newCondition() 方法,这就是上面说的ReentrantLock里面设置内部Condititon类。由于ReentrantLock实现了Lock接口,因此它必须实现该方法,具体如下:

public Condition newCondition() {        return sync.newCondition();    }

返回Condition类的一个实例。

2 ReentrantLock其它方法介绍

在介绍它的其它方法前,要先明白它的使用方法,以下JDK中的建议:

class X {     private final ReentrantLock lock = new ReentrantLock();      // ...        public void m() {       lock.lock();  // block until condition holds       try {        // ... method body        } finally {        lock.unlock()      }    }

建议用try,在finally里面一定要释放锁,防止被中断时锁没释放,造成死锁

lock()

public void lock() {        sync.lock();    }

如果该锁没被其它线程获得,则立即返回;并且把 lock hold count的值变位1.

unlock()

public void unlock() {        sync.release(1);    }

如果当前线程是该锁的持有者,则保持计数递减。 如果保持计数现在为零,则锁定被释放。 如果当前线程不是该锁的持有者,则抛出IllegalMonitorStateException 。

isFair()

public final boolean isFair() {        return sync instanceof FairSync;    }

判断该锁是不是公平锁

newCondition()

public Condition newCondition() {        return sync.newCondition();    }

返回新的ConditionObject对象。

Condition接口中的方法

  • await(): void await() throws InterruptedException;

    Condition接口中的方法,导致当前线程等到发信号。

  • siginal()
/**         * Moves the longest-waiting thread, if one exists, from the         * wait queue for this condition to the wait queue for the         * owning lock.         *         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}         *         returns {@code false}         */        public final void signal() {            if (!isHeldExclusively())                throw new IllegalMonitorStateException();            Node first = firstWaiter;            if (first != null)                doSignal(first);        }

唤醒一个等待该条件的线程去获得锁(第一个)。

  • signalAll():唤醒所有等待线程。

3 ReentrantLock完整实例介绍

package chapter10.reentrantlock;import java.util.Arrays;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*模拟转账,把钱从一个账户转到另一个账户 * */public class ReentrantLockUse {    public static final int NACCOUNTS = 100;    public static final double INITIAL_BALANCE = 1000;    public static final double MAX_AMOUNT = 1000;    public static final int DELAY = 10;        public static void main(String[] args) {        Bank bank = new Bank(NACCOUNTS,INITIAL_BALANCE);        for(int i = 0 ; i < NACCOUNTS ; i++) {            int fromAccount = i ;            Runnable r = () ->{//lambda表达式                try {                    while(true) {                        int toAccount  = (int) (bank.size()*Math.random());                        double amount = MAX_AMOUNT * Math.random();                        bank.transfer(fromAccount, toAccount, amount);                        Thread.sleep((int)(DELAY*Math.random()));                    }                }                catch(InterruptedException e) {                                    }            };            Thread t = new Thread(r);//新建线程            t.start();        }    }}class Bank{    private final double[] account;//账户    private Lock bankLock ;     //重复锁    private Condition sufficientFunds;  //条件对象                public Bank(int n, double initialBalance) {        account = new double[n];                Arrays.fill(account, initialBalance);        bankLock = new ReentrantLock();  //构造对象时,实例化锁        sufficientFunds = bankLock.newCondition();//新建条件对象    }    /*转账,把from账户里面的钱转到to里面,金额是amount*/    public void transfer(int from , int to,double amount) {                bankLock.lock();        try {            while(account[from] < amount) {                  sufficientFunds.await();            }            System.out.println(Thread.currentThread());            account[from] -=amount;            System.out.printf("%10.2f from %d to %d ",amount,from,to);            account[to] +=amount;            System.out.printf(" Total Balance : %10.2f%n", getTotalBalance());            sufficientFunds.signalAll();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        finally {            bankLock.unlock();        }    }    /*做的所有账户总额*/    public double getTotalBalance() {        bankLock.lock();        try {             double sum = 0;             for(double a : account) {                 sum +=a;             }             return sum;        }        finally {            bankLock.unlock();        }    }        public int size() {        return account.length;    }}

执行结果

reentrantLOck执行结果

结果分析

循环建立100个线程,每个线程都在不停转账,由于ReentrantLock的使用,任何时刻所有账户的总额都保持不变。另外,把钱amount从A账户转到B账户,要先判断A账户中是否有这么多钱,不过没有就调用条件对象ConditionObject中的await()方法,放弃该线程,等该其它线程转钱进来;转钱完成后调用.siginalAll()。

转载于:https://www.cnblogs.com/java-learner/p/9651675.html

你可能感兴趣的文章
python--字典类型
查看>>
Powershell 批量重命名
查看>>
zabbix proxy搭建及其排错
查看>>
如何利用报表工具FineReport实现报表列的动态展示
查看>>
IC卡制作常识概述
查看>>
centos EMQTTD 集群安装配置与测试验证
查看>>
MYSQL常用的架构和优化及常用的配置详解及MySQL数据库主从同步延迟原理
查看>>
Renewing a self-signed certificate in SBS 2003
查看>>
分布式文件系统之配置DFS复制
查看>>
【Maclean技术分享】开Oracle调优鹰眼,深入理解AWR性能报告 第二讲
查看>>
linux的启动过程详解
查看>>
MySQL多线程备份工具:mydumper
查看>>
如何配置用于运行数据库的虚拟机。
查看>>
SVN笔记
查看>>
border 外边框
查看>>
二进制安装 MySQL 格式待整理
查看>>
华为NE40黑名单与全局策略
查看>>
Function Based Indexes and Global Temporary Tables
查看>>
云数据中心的网络架构
查看>>
二进制安装mariadb
查看>>