深入研究ReentrantLock(重入锁)

2024-03-11 21:48

本文主要是介绍深入研究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();  
    }   
}  

这篇关于深入研究ReentrantLock(重入锁)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/799179

相关文章

可重入锁和不可重入锁概念和区别

可重入锁就是一个类的A、B两个方法,A、B都有获得统一把锁,当A方法调用时,获得锁,在A方法的锁还没有被释放时,调用B方法时,B方法也获得该锁。 这种情景,可以是不同的线程分别调用这个两个方法。也可是同一个线程,A方法中调用B方法,这个线程调用A方法。 不可重入锁就是一个类的A、B两个方法,A、B都有获得统一把锁,当A方法调用时,获得锁,在A方法的锁还没有被释放时,调用B方法时,B方法也获得不

java线程 yield,sleep,join,synchronized wait notify notifyAll,ReentrantLock lock condition, 生产者消费者

yield,sleep,join yield,join,sleep,join是Thread中的方法,不需要 在synchronized 代码块中调用,和synchronized 没关系,也不会释放锁。 Thread.sleep(100);Thread.yield();Thread t;t.join(); (1)yield()不一定保证让出cpu yield()只是使当前线程重新回

synchronized wait()/notify 对比 ReentrantLock await()/signal()

结论 synchronized synchronized 配合 wait()/notify 无法实现精准唤醒线程 ReentrantLock ReentrantLock 配合 Condition await()/signal() 可以实现精准唤醒线程 (指唤醒指定的线程) ReentrantLock 如何实现精准唤醒线程 一个 lock 配合多个 Condition, 且

深入研究java.lang.Runtime类

http://lavasoft.blog.51cto.com/62575/15565/

ReentrantLock的lockInterruptibly()理解

ReentrantLock锁有几种:lock、tryLock、tryLock(long timeout, TimeUnit unit)、lockInterruptibly。 lock 阻塞等待获取锁,优先考虑获取锁,待获取锁成功后,才响应中断。 lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。  tryLock() 是一个有返回值的方法,试图申请一个锁

【并发】Lock与ReentrantLock

1 Lock基本使用 Lock能实现代码同步,它比synchronized更具灵活性,什么时候锁住,什么时候释放锁等都是看得见的,使用时必须使用try{}finally{},意思是万一发生异常或者错误都可以释放锁。 try{}finally{//释放锁} 使用示例 public class SaleTicket implements Runnable {private int tic

可重入VI,VI模板和动态VI之间的差异 转

可重入VI 当您想要同时运行同一VI的多个实例时,将使用可重入VI。当VI不可重入时,VI只有一个数据空间。因此,一次只能有一个调用者运行VI,因此调用者可能必须“等待轮到它”时才能使用VI。这是VI的默认选项,但您可以将VI设置为可重入。其中有两种类型的重入:共享 和 预分配。 共享的可重入VI 如果选择了共享克隆可重入执行选项,则VI会具有一个数据空间池(与克隆池相同)。最初在调用VI开始时

绝大多数程序员没有这么深入研究过System.out.println()!

点击上方“朱小厮的博客”,选择“设为星标” 回复”1024“获取独家整理的学习资料 来源:http://1t.click/k87 什么是System.out.println() System.out.println是一个Java语句,一般情况下是将传递的参数,打印到控制台。 System:是 java.lang包中的一个final类。根据javadoc,“java.lang.System该

C语言信号编程深入研究

信号是操作系统向进程发送的一种软件中断,用于通知进程发生了某种特殊情况或异常事件。本篇文章将详细介绍如何使用C语言处理信号,包括基本的信号处理、自定义信号处理函数以及一些高级主题。 1. 引言 信号处理是操作系统与进程之间的一种通信机制。在C语言中,信号处理通常涉及捕获特定信号并在程序中执行相应的处理动作。本指南旨在提供一个全面的框架,帮助读者深入了解信号处理的基本原理和实践技巧。 2.

多线程篇(基本认识 - 公平锁 非公平锁、独占锁 共享锁、可重入锁、自旋锁)(持续更新迭代)

目录 锁一:公平锁与非公平锁 前言 一、Lock 锁接口 二、公平锁 1. 简介 三、非公平锁 1. 简介 四、JUC 1. ReentranLock 公平锁 非公平锁 锁二:独占锁 & 共享锁 前言 一、简介 二、代码示例 1. 未加锁状态 2. 加锁状态 锁三:可重入锁 前言 一、简介 二、代码示例 锁四:自旋锁 一、前言 二、问题思考 三、思