zoukankan      html  css  js  c++  java
  • JAVA中ReentrantLock详解(转)

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

    前言:本文解决的问题

    • 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()。

  • 相关阅读:
    Java编程思想:第8章 多态
    Java编程思想:第7章 复用类
    Java编程思想:第6章 访问权限控制
    Java编程思想:第5章 初始化与清理
    Java编程思想:第4章 控制执行流程
    Java编程思想:第3章 操作符
    sqoop常用命令记录
    sqoop从hive导出到mysql报错: failed with state FAILED due to: Task failed
    flink-sql解析canal-json实现实时同步
    hive爬坑
  • 原文地址:https://www.cnblogs.com/ffaiss/p/11431428.html
Copyright © 2011-2022 走看看