`
talentluke
  • 浏览: 604705 次
  • 性别: 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();  
    }   
}  

分享到:
评论

相关推荐

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

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

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

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

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

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

    one lock.zip

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

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

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

    ConcurrentDemo.zip

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

    实战Java高并发程序设计模式视频

    3. **锁机制**:掌握synchronized关键字的使用,包括可重入锁、锁对象、锁升级和锁优化。同时,学习ReentrantLock、ReadWriteLock等高级锁的使用场景和优势。 4. **并发集合**:理解并使用并发安全的数据结构,如...

    juc入门案例演示代码

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

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

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

    JUC并发工具包实例.zip

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

    ArrayBlockingQueue源码解析-动力节点共

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

    线程经典案例

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

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

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

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

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

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

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

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

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

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

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

    java线程入门教程,涉及线程基本知识,显浅易懂..zip

    - **Lock接口与ReentrantLock**:提供更灵活的线程同步机制,如公平锁、非公平锁、可重入锁、读写锁等。 4. **线程优先级** Java线程有10个优先级,从MIN_PRIORITY(1)到MAX_PRIORITY(10),默认是NORM_PRIORITY(5)...

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

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

Global site tag (gtag.js) - Google Analytics