JAVA中ReentrantLock详解

前言:本文解决的问题

  • 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和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;
    }
}

执行结果

结果分析

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

原文地址:https://www.cnblogs.com/java-learner/p/9651675.html

时间: 2024-11-10 13:24:20

JAVA中ReentrantLock详解的相关文章

【转】 java中HashMap详解

原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 H

java中运算符详解

前言 运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算.JAVA中常见的运算符有很多种,大致分为以下几种,常见的几种运算符如下图: 算术运算符 加.减.乘.除.求余.例++.--.%./.  赋值运算符 为变量或常量起到赋值作用的.例如=.+=.*= 关系运算符 判断数据大小的,结果为一个布尔值.例如>.>=.!=.== 逻辑运算符 进行逻辑运算,运算的成员为布尔值,结果也为布尔值.例如&&.||.!. 条件运算符 也称三目运算符,表达式为(a<b)?a:b

JAVA中priorityqueue详解

Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度,将让读者建立对PriorityQueue建立清晰而深入的认识. 总体介绍 前面以Java?ArrayDeque_为例讲解了_Stack_和_Queue,其实还有一种特殊的队列叫做_PriorityQueue_,即优先队列.优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每

java中HashSet详解

HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码: Java代码   public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { // 使用 HashM

java中HashMap详解

原文:http://alex09.iteye.com/blog/539545 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 通过 HashMap.HashSet 的源代码分析

Java中MVC详解以及优缺点总结

 概念:  MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务数据.逻辑.界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑.MVC被独特的发展起来用于映射传统的输入.处理和输出功能在一个逻辑的图形化用户界面的结构中. 最典型的MVC就是Jsp + Servlet + JavaBean的模式 MVC开始是存在于桌面程

Java中Volatile详解

当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写.这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致. 要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取.一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰. Volatile修饰的成员变量在每次被线程访问

java中HashMap详解(转)

HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 通过 HashMap.HashSet 的源代码分析其 Hash 存储机制 实际上,HashSet 和 HashMap 之间有很多

java中HashSet详解(转)

HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码: Java代码   public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable { // 使用 HashM