zoukankan      html  css  js  c++  java
  • java多线程系列6 synchronized 加强版 ReentrantLock

    ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。ReenreantLock类的常用方法有:  

     ReentrantLock() : 创建一个ReentrantLock实例    

    ReentrantLock(boolean fair)  是否是公平锁,默认不是 【实际开发中 一般不开启公平】

     lock() : 获得锁        

     unlock() : 释放锁

    下面代码演示 ReentrantLock 实现的累加器

    public class RentLack implements Runnable {
    	ReentrantLock lock = new ReentrantLock();
    
    	private int count = 0;
    	public int getCount()
    	{
    		return count;
    	}
    	@Override
    	public void run() {
    
    		for (int i = 0; i < 10000; i++) {
    			lock.lock();
    			try {
    				count++;
    			} finally {
    				lock.unlock(); //释放锁一定要写在finally里面
    			}
    		}
    
    	}
    
    	public static void main(String[] args) throws Exception {
    		RentLack rentLack = new RentLack();
    		Thread t1 = new Thread(rentLack);
    		Thread t2 = new Thread(rentLack);
    		t1.start();
    		t2.start();
    		t1.join();
    		t2.join();
    		System.out.println(rentLack.getCount());
    	}
    }
    

      

     原理解析 ----------

    说道ReentrantLock,不得不谈AbstractQueuedSynchronizedAQS抽象的队列式的同步器),

     AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch...

     -----深入了解AQS 请google相关信息

     当我们调用 ReentrantLock lock = new ReentrantLock(); 看看具体是怎么做的?

     private final Sync sync;
      public ReentrantLock() {
            sync = new NonfairSync();
        }
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
    }  

    可以看到,它调用是 上面两个类  他们的关系如下

    ReentrantLock实现了Lock接口,获取锁是通过lock方法来实现的。

    那当我们调用lock的时候,具体都做了什么呢? 这里只分析不公平方式

     具体代码如下

       final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }
    
    =======
       public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    
    ==========
    下来看看 非公平的方式的tryAcquire
    
     final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
    //state字段,在ReentrantLock中表示锁被持有的次数,它是一个volatile类型的整型值,因此对它的修改可以保证其他线程可以看到 int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }

      

     执行流程如下 

      如果没有线程占有该资源,直接当前线程占有。

      如果占用了,执行acquire(1)

    1. 调用tryAcquire()尝试直接去获取资源,如果成功则直接返回;
    2. 获取失败,则addWaiter()将该线程加入等待队列
    3. acquireQueued()使线程在等待队列中休息,有机会时(轮到自己,会被unpark())会去尝试获取资源。获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。

              4.自我中断

      在tryAcquire方法中可以看到,同一个线程获得锁以后可以多次重入,但是需要多次unlock哈。tryAcquire执行流程如下

     1、首先判断锁有没有被持有,如果被持有,就判断持有锁的线程是不是当前线程,如果不是就啥也不做,返回获取失败,如果是就增加重入数,返回成功获取;

    2、如果锁没有被任何线程持有(c==0),直接将当前线程设置为锁的持有者。

    ======================

    总结:关于 synchronized , ReentrantLock 究竟用谁的问题?

          synchronized 能解决的问题,ReentrantLock 都能解决  。

          在性能上,Jdk8 对synchronized进行了优化,性能上差别不大。

  • 相关阅读:
    人的一生为什么要努力 &1
    数据库_数据库系统概论
    电子商务安全
    虚拟专用网技术
    人的一生为什么要努力
    数据备份与恢复技术
    入侵检测技术
    简历模板连接
    防火墙技术
    字节与位
  • 原文地址:https://www.cnblogs.com/javabigdata/p/6854402.html
Copyright © 2011-2022 走看看