GVKun编程网logo

Java中的“ ReentrantLock”是什么意思?(java reentrantlock用法)

19

在这篇文章中,我们将带领您了解Java中的“ReentrantLock”是什么意思?的全貌,包括javareentrantlock用法的相关情况。同时,我们还将为您介绍有关JavaLock示例-Ree

在这篇文章中,我们将带领您了解Java中的“ ReentrantLock”是什么意思?的全貌,包括java reentrantlock用法的相关情况。同时,我们还将为您介绍有关Java Lock示例 - ReentrantLock、java ReentrantLock、java ReentrantLock详解、Java 中的显示锁 ReentrantLock 使用与原理的知识,以帮助您更好地理解这个主题。

本文目录一览:

Java中的“ ReentrantLock”是什么意思?(java reentrantlock用法)

Java中的“ ReentrantLock”是什么意思?(java reentrantlock用法)

重入意味着锁定是在每个线程而不是每个调用的基础上获得的。

由于内在锁由线程持有,这是否意味着线程运行一次等于调用基础?

谢谢,这似乎意味着:在线程中,如果我lockAdoA调用function的流程函数时获得了锁doB,并且doB还需要一个锁lockA,那么将会有一个可重入的过程。在Java中,这种现象是按线程获取的,因此我不需要考虑死锁吗?

答案1

小编典典

重入意味着锁定是在每个线程而不是每个调用的基础上获得的。

这是一个误导性的定义。这是真的(有点),但它没有切中要害。

可重入意味着(通常使用CS / IT术语)您正在做某事,而当您仍在做时,您又在做一次。对于锁,这意味着您 在单个线程上执行以下操作

  1. 获取“ foo”的锁定。
  2. 做一点事
  3. 获取“ foo”的锁定。请注意,我们尚未释放先前获得的锁。
  4. 释放对“ foo”的锁定
  5. 释放对“ foo”的锁定

使用可重入锁/锁定机制,获取相同锁的尝试将成功,并将增加属于该锁的内部计数器。仅当锁的当前持有者两次释放锁时,锁才会释放。

这是Java中使用原始对象锁/监视器的示例,它们是可重入的:

Object lock = new Object();...synchronized (lock) {    ...    doSomething(lock, ...)    ...}public void doSomething(Object lock, ...) {    synchronized (lock) {        ...    }}

可重入锁的替代方法是不可重入锁,在这种情况下,线程尝试获取已持有的锁将是一个错误。

使用可重入锁的优势在于,您不必担心由于意外获取已经持有的锁而导致失败的可能性。不利的一面是,您不能假设调用的内容不会改变锁旨在保护的变量的状态。但是,通常这不是问题。锁通常用于防止
其他 线程进行的并发状态更改。


所以我不用考虑僵局了吗?

是的你是。

线程不会对自己死锁(如果锁是可重入的)。但是,如果还有其他线程可能在尝试锁定的对象上具有锁定,则可能会出现死锁。

Java Lock示例 - ReentrantLock

Java Lock示例 - ReentrantLock

引言

在多线程环境下,通常我们使用 synchronized 关键字来保证线程安全。

大多数情况下,用 synchronized 关键字就足够了,但它也有一些缺点, 所以在 Java Concurrency 包中引入了 Lock API 。从Java 1.5版开始在 java.util.concurrent.locks 包中提供了处理并发的 Concurrency API 的 Lock 锁接口和一些实现类来改进 Object 锁定机制。

Java Lock API中的一些重要接口和类

Java Lock API中的一些重要接口和类是:

  • 锁(Lock):这是Lock API的基本接口。它提供了 synchronized 关键字的所有功能,以及为锁定创建不同条件的其他方法,为线程等待锁定提供超时功能。一些重要的方法是 lock() 获取锁,unlock() 释放锁,tryLock() 等待锁定一段时间,newCondition() 创建条件等。
  • 条件(Condition):条件对象类似于对象等待通知( Object wait-notify)模型,具有创建不同等待集的附加功能。Condition 对象始终由 Lock 对象创建。一些重要的方法是 await(),类似于Object.wait() 和 signal(),signalAll(),类似于 Object.notify() 和 Object.notifyAll() 方法。
  • 读写锁(ReadWriteLock):它包含一对关联的锁,一个用于只读操作,另一个用于写入。只要没有写入线程,读锁可以由多个读取线程同时保持。写锁是独占的。
  • 重入锁(ReentrantLock):这是最广泛使用的 Lock 接口实现类。此类以与 synchronized 关键字类似的方式实现 Lock 接口。除了 Lock 接口实现之外,ReentrantLock 还包含一些实用程序方法来获取持有锁的线程,等待获取锁线程等。

synchronized 块

synchronized 块本质上是可重入的,即如果一个线程锁定了监视对象,并且另一个同步块需要锁定在同一个监视对象上,则线程可以进入该代码块。我认为这就是类名是ReentrantLock的原因。让我们通过一个简单的例子来理解这个特性。

public class Test{

public synchronized foo(){
    //do something
    bar();
  }

  public synchronized bar(){
    //do some more
  }
}

如果一个线程进入 foo(),它就会锁定Test对象,所以当它尝试执行 bar() 方法时,允许该线程执行 bar() 方法,因为它已经在 Test 对象上持有锁,即与 synchronized(this) 效果是一样的。

Java Lock 示例 - Java 中的 ReentrantLock

现在让我们看一个简单的例子,我们将使用 Java Lock API 替换 synchronized 关键字。

假设我们有一个 Resource 类,其中包含一些操作,我们希望它是线程安全的,以及一些不需要线程安全的方法。

package com.journaldev.threads.lock;

public class Resource {

    public void doSomething(){
        //do some operation, DB read, write etc
    }
    
    public void doLogging(){
        //logging, no need for thread safety
    }
}

现在假设我们有一个 Runnable 类,我们将使用 Resource 方法。

package com.journaldev.threads.lock;

public class SynchronizedLockExample implements Runnable{

    private Resource resource;
    
    public SynchronizedLockExample(Resource r){
        this.resource = r;
    }
    
    @Override
    public void run() {
        synchronized (resource) {
            resource.doSomething();
        }
        resource.doLogging();
    }
}

请注意,我使用 synchronized 块来获取 Resource 对象上的锁。我们可以在类中创建一个虚拟对象,并将其用于锁定的目的。

现在让我们看看我们如何使用 Java Lock API 并重写上面的程序而不使用 synchronized 关键字。我们将在Java 中使用 ReentrantLock。

package com.journaldev.threads.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrencyLockExample implements Runnable{

    private Resource resource;
    private Lock lock;
    
    public ConcurrencyLockExample(Resource r){
        this.resource = r;
        this.lock = new ReentrantLock();
    }
    
    @Override
    public void run() {
        try {
            if(lock.tryLock(10, TimeUnit.SECONDS)){
               resource.doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //release lock
            lock.unlock();
        }
        resource.doLogging();
    }

}

正如你所看到的,我正在使用 tryLock() 方法来确保我的线程只等待一定的时间,如果它没有获得对象的锁定,它只是记录和退出。另一个要注意的重点是使用 try-finally 块来确保即使 doSomething() 方法调用抛出任何异常也会释放锁定。

Java Lock 与 synchronized 比较

基于以上细节和程序,我们可以很容易地得出 Java Lock 和同步之间的以下差异。

  • Java Lock API 为锁定提供了更多的可见性和选项,不像在线程可能最终无限期地等待锁定的同步,我们可以使用tryLock() 来确保线程仅等待特定时间。
  • 同步关键字的代码更清晰,更易于维护,而使用 Lock,我们不得不尝试使用 try-finally 块来确保即使在 lock() 和 unlock() 方法调用之间抛出异常也会释放 Lock。
  • 同步块或方法只能覆盖一种方法,而我们可以在一种方法中获取锁,并使用 Lock API 在另一种方法中释放它。
  • synchronized 关键字不提供公平性,而我们可以在创建 ReentrantLock 对象时将公平性设置为 true,以便最长等待的线程首先获得锁定。
  • 我们可以为 Lock 创建不同的等待条件(Condition),不同的线程可以针对不同的条件来 await() 。

这就是 Java Lock 示例,Java 中的 ReentrantLock 以及使用 synchronized 关键字的比较分析。

作 者:

关于Pankaj
8087ef00af2610b870ada12392286695?s=120&d=blank&r=g
如果你走得这么远,那就意味着你喜欢你正在读的东西。为什么不直接在Google Plus,Facebook或Twitter上与我联系。我很想直接听到你对我的文章的想法和意见。

最近我开始创建视频教程,所以请在Youtube上查看我的视频。

java ReentrantLock

java ReentrantLock

介绍

ReentrantLock称为重入锁,比内部锁synchonized拥有更强大的功能,它可中断、可定时、设置公平锁

【注】使用ReentrantLock时,一定要释放锁,一般释放放到finnal里写。

提供以下重要的方法

  • lock():获得锁,如果锁已被占用,则等待
  • lockInterruptibly():获得锁,但有限响应中断
  • unlock():释放锁
  • tryLock():尝试获取锁。如果获得,返回true;否则返回false
  • tryLock(long time, TimeUnit unit):在给定时间内获得锁。如果获得返回true;否则返回false

示例

例子1

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    ReentrantLock lock;

    ReentrantLockTest(ReentrantLock lock) {
        this.lock = lock;
    }

    private Runnable getRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                while(true) {
                    try {
                        if (lock.tryLock()) {
                            try {
                                System.out.println("Locked:" + Thread.currentThread().getName());
                                Thread.sleep(800);
                            } finally {
                                lock.unlock();
                                System.out.println("UnLocked:" + Thread.currentThread().getName());
                            }
                            System.out.println("break before");
                            break;
                        } else {
                            //System.out.println("Unable to lock " + Thread.currentThread().getName());
                        }

                    } catch (InterruptedException e){
                        System.out.println(Thread.currentThread() + " is Interupted");
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        ReentrantLockTest test = new ReentrantLockTest(lock);
        ReentrantLockTest test2 = new ReentrantLockTest(lock);
        Thread thread1 = new Thread(test.getRunnable(), "firstThread");
        Thread thread2 = new Thread(test2.getRunnable(), "secondThread");

        thread1.start();
        thread2.start();
        try {
            Thread.sleep(300);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("interupt begin");
        thread2.interrupt();
        System.out.println("interupt end");
    }
}

一次执行结果:

Locked:firstThread
interupt begin
interupt end
UnLocked:firstThread
break before
Locked:secondThread
UnLocked:secondThread
Thread[secondThread,5,main] is Interupted
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.jihite.templet.JavaBase.ReentrantLockTest$1.run(ReentrantLockTest.java:23)
	at java.lang.Thread.run(Thread.java:748)
Locked:secondThread
UnLocked:secondThread
break before

分析:firstThread执行,secondThread不停的判断是否可以获得锁,当firstThread执行完,secondThread执行后被打断

例子2

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    ReentrantLock lock;

    ReentrantLockTest(ReentrantLock lock) {
        this.lock = lock;
    }

    private Runnable getRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                while(true) {
                    try {
                        if (lock.tryLock(700, TimeUnit.MILLISECONDS)) {
                            try {
                                System.out.println("Locked:" + Thread.currentThread().getName());
                                Thread.sleep(800);
                            } finally {
                                lock.unlock();
                                System.out.println("UnLocked:" + Thread.currentThread().getName());
                            }
                            System.out.println("break before");
                            break;
                        } else {
                            //System.out.println("Unable to lock " + Thread.currentThread().getName());
                        }

                    } catch (InterruptedException e){
                        System.out.println(Thread.currentThread() + " is Interupted");
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        ReentrantLockTest test = new ReentrantLockTest(lock);
        ReentrantLockTest test2 = new ReentrantLockTest(lock);
        Thread thread1 = new Thread(test.getRunnable(), "firstThread");
        Thread thread2 = new Thread(test2.getRunnable(), "secondThread");

        thread1.start();
        thread2.start();
        try {
            Thread.sleep(300);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("interupt begin");
        thread2.interrupt();
        System.out.println("interupt end");
    }
}

一次执行结果

Locked:firstThread
interupt begin
interupt end
Thread[secondThread,5,main] is Interupted
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireNanos(AbstractQueuedSynchronizer.java:936)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos(AbstractQueuedSynchronizer.java:1247)
	at java.util.concurrent.locks.ReentrantLock.tryLock(ReentrantLock.java:442)
	at com.jihite.templet.JavaBase.ReentrantLockTest$1.run(ReentrantLockTest.java:19)
	at java.lang.Thread.run(Thread.java:748)
Locked:secondThread
UnLocked:firstThread
break before
UnLocked:secondThread
break before

分析:firstThread执行,secondThread等待,等待过程被打断。打断后firstThread执行结束了,secondThread得到锁,继续执行

例子3

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest2 {
    ReentrantLock lock;

    ReentrantLockTest2(ReentrantLock lock) {
        this.lock = lock;
    }

    private Runnable getRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        try {
                            lock.lock();
//                            lock.lockInterruptibly();
                            System.out.println("Locked:" + Thread.currentThread().getName());
                            Thread.sleep(800);
                            break;
                        } finally {
                            lock.unlock();
                            System.out.println("UnLocked:" + Thread.currentThread().getName());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        ReentrantLockTest2 test = new ReentrantLockTest2(lock);
        ReentrantLockTest2 test2 = new ReentrantLockTest2(lock);
        Thread thread1 = new Thread(test.getRunnable(), "firstThread");
        Thread thread2 = new Thread(test2.getRunnable(), "secondThread");

        thread1.start();
        thread2.start();
        try {
            Thread.sleep(600);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("interupt begin");
        thread2.interrupt();
        System.out.println("interupt end");
    }
}

一次执行结果

Locked:firstThread
interupt begin
interupt end
UnLocked:firstThread
Locked:secondThread
UnLocked:secondThread
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.jihite.templet.JavaBase.ReentrantLockTest2$1.run(ReentrantLockTest2.java:22)
	at java.lang.Thread.run(Thread.java:748)
Locked:secondThread
UnLocked:secondThread

分析:firstThread先获得锁执行,secondThread在等待,此时中断并未打断等待。firstThread执行完,secondThread获取后被打断

例子4

public class ReentrantLockTest2 {
    ReentrantLock lock;

    ReentrantLockTest2(ReentrantLock lock) {
        this.lock = lock;
    }

    private Runnable getRunnable() {
        return new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        try {
//                            lock.lock();
                            lock.lockInterruptibly();
                            System.out.println("Locked:" + Thread.currentThread().getName());
                            Thread.sleep(800);
                            break;
                        } finally {
                            lock.unlock();
                            System.out.println("UnLocked:" + Thread.currentThread().getName());
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        ReentrantLockTest2 test = new ReentrantLockTest2(lock);
        ReentrantLockTest2 test2 = new ReentrantLockTest2(lock);
        Thread thread1 = new Thread(test.getRunnable(), "firstThread");
        Thread thread2 = new Thread(test2.getRunnable(), "secondThread");

        thread1.start();
        thread2.start();
        try {
            Thread.sleep(600);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("interupt begin");
        thread2.interrupt();
        System.out.println("interupt end");
    }
}

一次执行结果

Locked:firstThread
interupt begin
interupt end
Exception in thread "secondThread" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
	at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
	at com.jihite.templet.JavaBase.ReentrantLockTest2$1.run(ReentrantLockTest2.java:25)
	at java.lang.Thread.run(Thread.java:748)

分析:lock.lockInterruptibly();在执行过程中可以响应中断时间

java ReentrantLock详解

java ReentrantLock详解

这篇文章主要介绍了java reentrantlock,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

介绍

reentrantlock称为重入锁,比内部锁synchonized拥有更强大的功能,它可中断、可定时、设置公平锁

【注】使用reentrantlock时,一定要释放锁,一般释放放到finnal里写。

提供以下重要的方法

lock():获得锁,如果锁已被占用,则等待

lockInterruptibly():获得锁,但有限响应中断

unlock():释放锁

tryLock():尝试获取锁。如果获得,返回true;否则返回false

tryLock(long time, TimeUnit unit):在给定时间内获得锁。如果获得返回true;否则返回false

示例

例子1

import java.util.concurrent.locks.reentrantlock; public class reentrantlockTest { reentrantlock lock; reentrantlockTest(reentrantlock lock) { this.lock = lock; } private Runnable getRunnable() { return new Runnable() { @Override public void run() { while(true) { try { if (lock.tryLock()) { try { System.out.println("Locked:" + Thread.currentThread().getName()); Thread.sleep(800); } finally { lock.unlock(); System.out.println("UnLocked:" + Thread.currentThread().getName()); } System.out.println("break before"); break; } else { //System.out.println("Unable to lock " + Thread.currentThread().getName()); } } catch (InterruptedException e){ System.out.println(Thread.currentThread() + " is Interupted"); e.printstacktrace(); } } } }; } public static void main(String[] args) { reentrantlock lock = new reentrantlock(); reentrantlockTest test = new reentrantlockTest(lock); reentrantlockTest test2 = new reentrantlockTest(lock); Thread thread1 = new Thread(test.getRunnable(), "firstThread"); Thread thread2 = new Thread(test2.getRunnable(), "secondThread"); thread1.start(); thread2.start(); try { Thread.sleep(300); }catch (InterruptedException e) { e.printstacktrace(); } System.out.println("interupt begin"); thread2.interrupt(); System.out.println("interupt end"); } }

一次执行结果:

Locked:firstThread

interupt begin

interupt end

UnLocked:firstThread

break before

Locked:secondThread

UnLocked:secondThread

Thread[secondThread,5,main] is Interupted

java.lang.InterruptedException: sleep interrupted

    at java.lang.Thread.sleep(Native Method)

    at com.jihite.templet.JavaBase.reentrantlockTest$1.run(reentrantlockTest.java:23)

    at java.lang.Thread.run(Thread.java:748)

Locked:secondThread

UnLocked:secondThread

break before

 分析:firstThread执行,secondThread不停的判断是否可以获得锁,当firstThread执行完,secondThread执行后被打断

例子2

import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.reentrantlock; public class reentrantlockTest { reentrantlock lock; reentrantlockTest(reentrantlock lock) { this.lock = lock; } private Runnable getRunnable() { return new Runnable() { @Override public void run() { while(true) { try { if (lock.tryLock(700, TimeUnit.MILLISECONDS)) { try { System.out.println("Locked:" + Thread.currentThread().getName()); Thread.sleep(800); } finally { lock.unlock(); System.out.println("UnLocked:" + Thread.currentThread().getName()); } System.out.println("break before"); break; } else { //System.out.println("Unable to lock " + Thread.currentThread().getName()); } } catch (InterruptedException e){ System.out.println(Thread.currentThread() + " is Interupted"); e.printstacktrace(); } } } }; } public static void main(String[] args) { reentrantlock lock = new reentrantlock(); reentrantlockTest test = new reentrantlockTest(lock); reentrantlockTest test2 = new reentrantlockTest(lock); Thread thread1 = new Thread(test.getRunnable(), "firstThread"); Thread thread2 = new Thread(test2.getRunnable(), "secondThread"); thread1.start(); thread2.start(); try { Thread.sleep(300); }catch (InterruptedException e) { e.printstacktrace(); } System.out.println("interupt begin"); thread2.interrupt(); System.out.println("interupt end"); } }

一次执行结果

Locked:firstThread

interupt begin

interupt end

Thread[secondThread,5,main] is Interupted

java.lang.InterruptedException

    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireNanos(AbstractQueuedSynchronizer.java:936)

    at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireNanos(AbstractQueuedSynchronizer.java:1247)

    at java.util.concurrent.locks.reentrantlock.tryLock(reentrantlock.java:442)

    at com.jihite.templet.JavaBase.reentrantlockTest$1.run(reentrantlockTest.java:19)

    at java.lang.Thread.run(Thread.java:748)

Locked:secondThread

UnLocked:firstThread

break before

UnLocked:secondThread

break before

分析:firstThread执行,secondThread等待,等待过程被打断。打断后firstThread执行结束了,secondThread得到锁,继续执行

例子3

import java.util.concurrent.locks.reentrantlock; public class reentrantlockTest2 { reentrantlock lock; reentrantlockTest2(reentrantlock lock) { this.lock = lock; } private Runnable getRunnable() { return new Runnable() { @Override public void run() { while (true) { try { try { lock.lock(); // lock.lockInterruptibly(); System.out.println("Locked:" + Thread.currentThread().getName()); Thread.sleep(800); break; } finally { lock.unlock(); System.out.println("UnLocked:" + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printstacktrace(); } } } }; } public static void main(String[] args) { reentrantlock lock = new reentrantlock(); reentrantlockTest2 test = new reentrantlockTest2(lock); reentrantlockTest2 test2 = new reentrantlockTest2(lock); Thread thread1 = new Thread(test.getRunnable(), "firstThread"); Thread thread2 = new Thread(test2.getRunnable(), "secondThread"); thread1.start(); thread2.start(); try { Thread.sleep(600); }catch (InterruptedException e) { e.printstacktrace(); } System.out.println("interupt begin"); thread2.interrupt(); System.out.println("interupt end"); } }

一次执行结果

Locked:firstThread

interupt begin

interupt end

UnLocked:firstThread

Locked:secondThread

UnLocked:secondThread

java.lang.InterruptedException: sleep interrupted

    at java.lang.Thread.sleep(Native Method)

    at com.jihite.templet.JavaBase.reentrantlockTest2$1.run(reentrantlockTest2.java:22)

    at java.lang.Thread.run(Thread.java:748)

Locked:secondThread

UnLocked:secondThread

分析:firstThread先获得锁执行,secondThread在等待,此时中断并未打断等待。firstThread执行完,secondThread获取后被打断

例子4

public class reentrantlockTest2 { reentrantlock lock; reentrantlockTest2(reentrantlock lock) { this.lock = lock; } private Runnable getRunnable() { return new Runnable() { @Override public void run() { while (true) { try { try { // lock.lock(); lock.lockInterruptibly(); System.out.println("Locked:" + Thread.currentThread().getName()); Thread.sleep(800); break; } finally { lock.unlock(); System.out.println("UnLocked:" + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printstacktrace(); } } } }; } public static void main(String[] args) { reentrantlock lock = new reentrantlock(); reentrantlockTest2 test = new reentrantlockTest2(lock); reentrantlockTest2 test2 = new reentrantlockTest2(lock); Thread thread1 = new Thread(test.getRunnable(), "firstThread"); Thread thread2 = new Thread(test2.getRunnable(), "secondThread"); thread1.start(); thread2.start(); try { Thread.sleep(600); }catch (InterruptedException e) { e.printstacktrace(); } System.out.println("interupt begin"); thread2.interrupt(); System.out.println("interupt end"); } }

一次执行结果

Locked:firstThread

interupt begin

interupt end

Exception in thread "secondThread" java.lang.IllegalMonitorStateException

    at java.util.concurrent.locks.reentrantlock$Sync.tryRelease(reentrantlock.java:151)

    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)

    at java.util.concurrent.locks.reentrantlock.unlock(reentrantlock.java:457)

    at com.jihite.templet.JavaBase.reentrantlockTest2$1.run(reentrantlockTest2.java:25)

    at java.lang.Thread.run(Thread.java:748)

分析:lock.lockInterruptibly();在执行过程中可以响应中断时间

以上所述是小编给大家介绍的java reentrantlock详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小编网站的支持!

Java 中的显示锁 ReentrantLock 使用与原理

Java 中的显示锁 ReentrantLock 使用与原理

考虑一个场景,轮流打印 0-100 以内的技术和偶数。通过使用 synchronize 的 wait,notify 机制就可以实现,核心思路如下:
使用两个线程,一个打印奇数,一个打印偶数。这两个线程会共享一个数据,数据每次自增,当打印奇数的线程发现当前要打印的数字不是奇数时,执行等待,否则打印奇数,并将数字自增 1,对于打印偶数的线程也是如此

//打印奇数的线程
private static class OldRunner implements Runnable{
    private MyNumber n;

    public OldRunner(MyNumber n) {
        this.n = n;
    }

    public void run() {
        while (true){
            n.waitToOld();  //等待数据变成奇数
            System.out.println("old:" + n.getVal());
            n.increase();
            if (n.getVal()>98){
                break;
            }
        }
    }
}
//打印偶数的线程
private static class EvenRunner implements Runnable{
    private MyNumber n;

    public EvenRunner(MyNumber n) {
        this.n = n;
    }

    public void run() {
        while (true){
            n.waitToEven();            //等待数据变成偶数
            System.out.println("even:"+n.getVal());
            n.increase(); 
            if (n.getVal()>99){
                break;
            }
        }
    }
}

共享的数据如下

private static class MyNumber{
    private int val;

    public MyNumber(int val) {
        this.val = val;
    }

    public int getVal() {
        return val;
    }
    public synchronized void increase(){
        val++;
        notify(); //数据变了,唤醒另外的线程
    }
    public synchronized void  waitToOld(){
        while ((val % 2)==0){
            try {
                System.out.println("i am "+Thread.currentThread().getName()+" ,but now is even:"+val+",so wait");
                wait(); //只要是偶数,一直等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized void waitToEven(){
        while ((val % 2)!=0){
            try {
                System.out.println("i am "+Thread.currentThread().getName()+"  ,but now old:"+val+",so wait");
                wait(); //只要是奇数,一直等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行代码如下

MyNumber n = new MyNumber(0);
Thread old=new Thread(new OldRunner(n),"old-thread");
Thread even = new Thread(new EvenRunner(n),"even-thread");
old.start();
even.start();

运行结果如下

i am old-thread ,but now is even:0,so wait
even:0
i am even-thread  ,but now old:1,so wait
old:1
i am old-thread ,but now is even:2,so wait
even:2
i am even-thread  ,but now old:3,so wait
old:3
i am old-thread ,but now is even:4,so wait
even:4
i am even-thread  ,but now old:5,so wait
old:5
i am old-thread ,but now is even:6,so wait
even:6
i am even-thread  ,but now old:7,so wait
old:7
i am old-thread ,but now is even:8,so wait
even:8

上述方法使用的是 synchronize 的 wait notify 机制,同样可以使用显示锁来实现,两个打印的线程还是同一个线程,只是使用的是显示锁来控制等待事件

private static class MyNumber{
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private int val;

    public MyNumber(int val) {
        this.val = val;
    }

    public int getVal() {
        return val;
    }
    public  void increase(){
        lock.lock();
        try {
            val++;
            condition.signalAll(); //通知线程
        }finally {
            lock.unlock();
        }

    }
    public  void  waitToOld(){
        lock.lock();
        try{
            while ((val % 2)==0){
                try {
                    System.out.println("i am should print old ,but now is even:"+val+",so wait");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            lock.unlock();
        }
    }
    public void waitToEven(){
        lock.lock(); //显示的锁定
        try{
            while ((val % 2)!=0){
                try {
                    System.out.println("i am should print even ,but now old:"+val+",so wait");
                    condition.await();//执行等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }finally {
            lock.unlock(); //显示的释放
        }

    }
}

同样可以得到上述的效果

显示锁的功能

显示锁在 java 中通过接口 Lock 提供如下功能

  • lock: 线程无法获取锁会进入休眠状态,直到获取成功
  • lockInterruptibly: 如果获取成功,立即返回,否则一直休眠到线程被中断或者是获取成功
  • tryLock:不会造成线程休眠,方法执行会立即返回,获取到了锁,返回 true, 否则返回 false
  • tryLock (long time, TimeUnit unit) throws InterruptedException : 在等待时间内没有发生过中断,并且没有获取锁,就一直等待,当获取到了,或者是线程中断了,或者是超时时间到了这三者发生一个就返回,并记录是否有获取到锁
  • unlock:释放锁
  • newCondition:每次调用创建一个锁的等待条件,也就是说一个锁可以拥有多个条件

Condition 的功能

接口 Condition 把 Object 的监视器方法 wait 和 notify 分离出来,使得一个对象可以有多个等待的条件来执行等待,配合 Lock 的 newCondition 来实现。

  • await: 使当前线程休眠,不可调度。这四种情况下会恢复 1:其它线程调用了 signal,当前线程恰好被选中了恢复执行;2: 其它线程调用了 signalAll;3: 其它线程中断了当前线程 4:spurious wakeup (假醒)。无论什么情况,在 await 方法返回之前,当前线程必须重新获取锁
  • awaitUninterruptibly: 使当前线程休眠,不可调度。这三种情况下会恢复 1:其它线程调用了 signal,当前线程恰好被选中了恢复执行;2: 其它线程调用了 signalAll;3:spurious wakeup (假醒)。
  • awaitNanos: 使当前线程休眠,不可调度。这四种情况下会恢复 1:其它线程调用了 signal,当前线程恰好被选中了恢复执行;2: 其它线程调用了 signalAll;3: 其它线程中断了当前线程 4:spurious wakeup (假醒)。5: 超时了
  • await (long time, TimeUnit unit) :与 awaitNanos 类似,只是换了个时间单位
  • awaitUntil (Date deadline): 与 awaitNanos 相似,只是指定日期之后返回,而不是指定的一段时间
  • signal: 唤醒一个等待的线程
  • signalAll: 唤醒所有等待的线程

ReentrantLock

从源码中可以看到,ReentrantLock 的所有实现全都依赖于内部类 Sync 和 ConditionObject。
Sync 本身是个抽象类,负责手动 lock 和 unlock,ConditionObject 则实现在父类 AbstractOwnableSynchronizer 中,负责 await 与 signal Sync 的继承结构如下

Sync 的两个实现类,公平锁和非公平锁

公平的锁会把权限给等待时间最长的线程来执行,非公平则获取执行权限的线程与线程本身的等待时间无关

默认初始化 ReentrantLock 使用的是非公平锁,当然可以通过指定参数来使用公平锁

public ReentrantLock() {
   sync = new NonfairSync();
}

当执行获取锁时,实际就是去执行 Sync 的 lock 操作:

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

对应在不同的锁机制中有不同的实现

  1. 公平锁实现
    final void lock() {
        acquire(1);
    }
    
  2. 非公平锁实现
    final void lock() {
        if (compareAndSetState(0, 1)) //先看当前锁是不是已经被占有了,如果没有,就直接将当前线程设置为占有的线程
            setExclusiveOwnerThread(Thread.currentThread());
        else        
            acquire(1); //锁已经被占有的情况下,尝试获取
    }
    

二者都调用父类 AbstractQueuedSynchronizer 的方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //一旦抢失败,就会进入队列,进入队列后则是依据FIFO的原则来执行唤醒
        selfInterrupt();
}

当执行 unlock 时,对应方法在父类 AbstractQueuedSynchronizer 中

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

公平锁和非公平锁则分别对获取锁的方式 tryAcquire 做了实现,而 tryRelease 的实现机制则都是一样的

公平锁实现 tryAcquire

源码如下

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState(); //获取当前的同步状态
    if (c == 0) {
        //等于0 表示没有被其它线程获取过锁
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            //hasQueuedPredecessors 判断在当前线程的前面是不是还有其它的线程,如果有,也就是锁sync上有一个等待的线程,那么它不能获取锁,这意味着,只有等待时间最长的线程能够获取锁,这就是是公平性的体现
            //compareAndSetState 看当前在内存中存储的值是不是真的是0,如果是0就设置成accquires的取值。对于JAVA,这种需要直接操作内存的操作是通过unsafe来完成,具体的实现机制则依赖于操作系统。
            //存储获取当前锁的线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        //判断是不是当前线程获取的锁
        int nextc = c + acquires;
        if (nextc < 0)//一个线程能够获取同一个锁的次数是有限制的,就是int的最大值
            throw new Error("Maximum lock count exceeded");
        setState(nextc); //在当前的基础上再增加一次锁被持有的次数
        return true;
    }
    //锁被其它线程持有,获取失败
    return false;
}

非公平锁实现 tryAcquire

获取的关键实现为 nonfairTryAcquire, 源码如下

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        //锁没有被持有
        //可以看到这里会无视sync queue中是否有其它线程,只要执行到了当前线程,就会去获取锁
        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;
}

释放锁的机制

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread()) //只能是线程拥有这释放
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        //当占有次数为0的时候,就认为所有的锁都释放完毕了
        free = true; 
        setExclusiveOwnerThread(null);
    }
    setState(c); //更新锁的状态
    return free;
}

从源码的实现可以看到

  • ReentrantLock 获取锁时,在锁已经被占有的情况下,如果占有锁的线程是当前线程,那么允许重入,即再次占有,如果由其它线程占有,则获取失败,由此可见,ReetrantLock本身对锁的持有是可重入的,同时是线程独占的
  • 公平与非公平就体现在,当执行的线程去获取锁的时候,公平的会去看是否有等待时间比它更长的,而非公平的就优先直接去占有锁

ReentrantLock 的 tryLock () 与 tryLock (long timeout, TimeUnit unit):

public boolean tryLock() {
//本质上就是执行一次非公平的抢锁
   return sync.nonfairTryAcquire(1); 
}

有时限的 tryLock 核心代码是 sync.tryAcquireNanos(1, unit.toNanos(timeout));,由于有超时时间,它会直接放到等待队列中,他与后面要讲的 AQS 的 lock 原理中 acquireQueued 的区别在于 park 的时间是有限的,详见源码 AbstractQueuedSynchronizer.doAcquireNanos

为什么需要显示锁

内置锁功能上有一定的局限性,它无法响应中断,不能设置等待的时间

今天关于Java中的“ ReentrantLock”是什么意思?java reentrantlock用法的分享就到这里,希望大家有所收获,若想了解更多关于Java Lock示例 - ReentrantLock、java ReentrantLock、java ReentrantLock详解、Java 中的显示锁 ReentrantLock 使用与原理等相关知识,可以在本站进行查询。

本文标签: