`
lionlx
  • 浏览: 287208 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(转)深入研究ReentrantLock(重入锁)

 
阅读更多
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
下面细细道来……

先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)

这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:

Java代码
public class Buffer {    
     
    private Object lock;    
     
    public Buffer() {    
        lock = this;    
    }    
     
    public void write() {    
        synchronized (lock) {    
            long startTime = System.currentTimeMillis();    
            System.out.println("开始往这个buff写入数据…");    
            for (;;)// 模拟要处理很长时间    
            {    
                if (System.currentTimeMillis()    
                        - startTime > Integer.MAX_VALUE)    
                    break;    
            }    
            System.out.println("终于写完了");    
        }    
    }    
     
    public void read() {    
        synchronized (lock) {    
            System.out.println("从这个buff读数据");    
        }    
    }    
}   


接着,我们来定义2个线程,一个线程去写,一个线程去读。

Java代码
public class Writer extends Thread {    
     
    private Buffer buff;    
     
    public Writer(Buffer buff) {    
        this.buff = buff;    
    }    
     
    @Override    
    public void run() {    
        buff.write();    
    }    
     
}    
     
public class Reader extends Thread {    
     
    private Buffer buff;    
     
    public Reader(Buffer buff) {    
        this.buff = buff;    
    }    
     
    @Override    
    public void run() {    
     
        buff.read();//这里估计会一直阻塞    
     
        System.out.println("读结束");    
     
    }    
     
}   


好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。

Java代码
public class Test {    
    public static void main(String[] args) {    
        Buffer buff = new Buffer();    
     
        final Writer writer = new Writer(buff);    
        final Reader reader = new Reader(buff);    
     
        writer.start();    
        reader.start();    
     
        new Thread(new Runnable() {    
     
            @Override    
            public void run() {    
                long start = System.currentTimeMillis();    
                for (;;) {    
                    //等5秒钟去中断读    
                    if (System.currentTimeMillis()    
                            - start > 5000) {    
                        System.out.println("不等了,尝试中断");    
                        reader.interrupt();    
                        break;    
                    }    
     
                }    
     
            }    
        }).start();    
     
    }    
}   


我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。

Java代码
import java.util.concurrent.locks.ReentrantLock;    
     
public class BufferInterruptibly {    
     
    private ReentrantLock lock = new ReentrantLock();    
     
    public void write() {    
        lock.lock();    
        try {    
            long startTime = System.currentTimeMillis();    
            System.out.println("开始往这个buff写入数据…");    
            for (;;)// 模拟要处理很长时间    
            {    
                if (System.currentTimeMillis()    
                        - startTime > Integer.MAX_VALUE)    
                    break;    
            }    
            System.out.println("终于写完了");    
        } finally {    
            lock.unlock();    
        }    
    }    
     
    public void read() throws InterruptedException {    
        lock.lockInterruptibly();// 注意这里,可以响应中断    
        try {    
            System.out.println("从这个buff读数据");    
        } finally {    
            lock.unlock();    
        }    
    }    
     
}   

当然,要对reader和writer做响应的修改

Java代码
public class Reader extends Thread {    
     
    private BufferInterruptibly buff;    
     
    public Reader(BufferInterruptibly buff) {    
        this.buff = buff;    
    }    
     
    @Override    
    public void run() {    
     
        try {    
            buff.read();//可以收到中断的异常,从而有效退出    
        } catch (InterruptedException e) {    
            System.out.println("我不读了");    
        }    
           
        System.out.println("读结束");    
     
    }    
     
}    
     
/**   
* Writer倒不用怎么改动   
*/    
public class Writer extends Thread {    
     
    private BufferInterruptibly buff;    
     
    public Writer(BufferInterruptibly buff) {    
        this.buff = buff;    
    }    
     
    @Override    
    public void run() {    
        buff.write();    
    }    
     
}    
     
public class Test {    
    public static void main(String[] args) {    
        BufferInterruptibly buff = new BufferInterruptibly();    
     
        final Writer writer = new Writer(buff);    
        final Reader reader = new Reader(buff);    
     
        writer.start();    
        reader.start();    
     
        new Thread(new Runnable() {    
     
            @Override    
            public void run() {    
                long start = System.currentTimeMillis();    
                for (;;) {    
                    if (System.currentTimeMillis()    
                            - start > 5000) {    
                        System.out.println("不等了,尝试中断");    
                        reader.interrupt();    
                        break;    
                    }    
     
                }    
     
            }    
        }).start();    
     
    }    
} 

这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。好奇的读者,肯定要探个究竟,为什么ReentrantLock能做到这点,接下来,我们去迷宫探险吧……
分享到:
评论

相关推荐

    各种锁汇总,乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁、行级锁等

    本文将深入探讨标题和描述中提及的各种锁,包括乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁以及行级锁。 1. **乐观锁**:乐观锁假设多线程环境中的冲突较少,所以在读取数据时不加锁,只有...

    偏向锁-轻量级锁-重量级锁

    在Java并发编程中,锁是控制多线程访问共享资源的...最后,对于源码阅读爱好者来说,深入研究JVM源码中的锁实现细节,如`ObjectMonitor`类,可以进一步提升对并发编程的理解,有助于在实际开发中更好地应用这些知识。

    并发编程从入门到放弃系列开始和结束.doc

    它具有可重入性,即一个线程可以多次获取同一锁,直到释放所有获取的锁。ReentrantLock还支持公平性和非公平性锁的配置,以及条件变量,这些特性使得ReentrantLock在某些场景下比`synchronized`更具优势。 除了...

    Java并发编程中Synchronized与Lock接口的实现原理、优化及应用

    此外,深入研究了 Java 线程池的设计模式、关键参数配置以及几种典型的预设线程池,同时讨论了 volatile 关键字的特征及与其他锁的区别。最后还提到了 ThreadLocal 的工作机制,强调了使用时应注意的关键点。 适合...

    深入研究Servlet线程安全性问题.pdf

    ### 深入研究Servlet线程安全性问题 #### 一、引言 Servlet技术作为Java Web开发中的核心组件之一,因其高效性和灵活性被广泛应用于Web应用程序的开发中。Servlet能够处理HTTP请求,并产生相应的响应。它的一个...

    one lock.zip

    在实际编程中,Java有`java.util.concurrent.locks`包提供了这些锁的实现,如`ReentrantLock`(可重入锁,互斥锁的一种),`ReadWriteLock`接口及其实现类`ReentrantReadWriteLock`,以及`Semaphore`类。C++的`std::...

    深入浅出_Java并发工具包原理讲解

    Java并发工具包(J.U.C)是Java编程语言中用于并发编程的一系列工具包的统称,它包含了一系列方便实现多线程编程的类和接口...对于J.U.C的深入研究和实践,不仅能够提升Java编程能力,也会对编程思想有更深层次的理解。

    ConcurrentDemo.zip

    ReentrantLock具有可重入性,允许同一个线程多次获取同一把锁,避免了死锁的情况。相比synchronized,ReentrantLock提供了更丰富的锁操作,比如公平锁、非公平锁的选择,以及尝试锁、可中断锁和定时锁等功能。而...

    juc入门案例演示代码

    例如,我们可以使用`ReentrantLock`(可重入锁)来实现更复杂的并发控制策略。这个`JUCLock`的代码可能是用来演示如何使用`Lock`接口及其相关类,如如何实现公平锁、非公平锁,或者如何配合`Condition`来实现线程间...

    JUC并发工具包实例.zip

    在本实例中,可能包括ReentrantLock(可重入锁)的示例,它比synchronized更灵活,可以进行更复杂的操作,如尝试加锁、公平锁与非公平锁的选择,以及锁的条件(Condition)管理。 3. **CountDownLatch**: ...

    最新AQS资料整理.pdf

    1. **独占锁与共享锁**:AQS支持两种类型的锁,独占锁(如ReentrantLock)和共享锁(如Semaphore)。独占锁只允许一个线程持有锁,而共享锁允许多个线程同时持有锁。 2. **状态变量state**:AQS的核心是state字段,...

    ArrayBlockingQueue源码解析-动力节点共

    在并发环境下,为了保证线程安全,ArrayBlockingQueue采用了ReentrantLock(可重入锁)来实现同步,并且在某些关键操作中使用了Condition(条件变量)来实现等待/通知机制。 1. 初始化:ArrayBlockingQueue在创建时...

    线程经典案例

    线程在Java编程中是并发处理的核心概念,它允许程序同时执行多个任务,极大地...在深入研究这两个案例的同时,还可以尝试其他的线程同步机制,如Semaphore信号量、ReentrantLock可重入锁等,进一步拓宽我们的知识面。

    Java并发编程最全面试题 123道

    10. Lock接口与ReentrantLock:对比synchronized,理解Lock接口的优势,重点研究可重入锁ReentrantLock的使用和特性。 三、并发集合 11. ConcurrentHashMap:深入理解并发容器ConcurrentHashMap的内部实现,包括...

    软件工程留学生Java并发编程教学研究.zip

    2. **同步机制**:Java提供了多种同步机制来控制多线程对共享资源的访问,如synchronized关键字、wait()和notify()方法、Lock接口(包括ReentrantLock可重入锁)以及Semaphore信号量等。学习如何避免并发环境下的...

    prioritylock:一个互斥锁,它将确保优先级值较高的请求在低优先级的请求获得锁之前先获得该锁

    在Java中,`java.util.concurrent.locks.Lock`接口及其实现如`ReentrantLock`提供了互斥锁的功能。 而PriorityLock则在基本的互斥锁基础上增加了一层优先级判断。在默认情况下,所有线程平等竞争锁,但PriorityLock...

    线程示例(有注释,包括同步,线程间通信)

    3. **Lock接口与ReentrantLock**:Java提供了更细粒度的锁控制,比如ReentrantLock(可重入锁),它比synchronized更灵活,支持公平锁、非公平锁、尝试加锁、定时加锁和可中断加锁等多种特性。ReentrantLock还提供了...

    高并发Java服务器设计研究.zip

    - `ReentrantLock`是可重入锁,提供了更细粒度的控制,如公平锁、非公平锁、读写锁等。 7. **响应式编程** - 响应式编程是一种编程范式,适用于处理高并发和流式数据。Java中的Reactor项目提供了响应式编程的支持...

    java+多线程+同步详解源码整理

    例如,`Thread`类的内部实现、`synchronized`关键字如何实现内存同步、`ReentrantLock`如何实现可重入和公平性等。你可以通过反编译JDK源码或查看OpenJDK源码库进行学习。 7. **实际应用** 多线程和同步在实际项目...

    线程之死锁处理方法代码.zip

    在Java编程中,多线程是并发执行任务的重要方式,然而随之而来的一个常见问题是线程死锁。死锁是指两个或多个线程...深入研究这些文件,将有助于你更好地理解和处理Java多线程环境中的死锁问题,提升系统性能和稳定性。

Global site tag (gtag.js) - Google Analytics