`
清风朗月
  • 浏览: 2037 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语

 
阅读更多
Java 5 引入的 Concurrent 并发库软件包中,提供了 ReentrantLock 可重入同步锁,用来替代 synchronized 关键字原语,并可提供更好的性能,以及更强大的功能。使用方法也很简单:

public final ReentrantLock lock=new ReentrantLock();
......
try {
    lock.lock();
    // 进入同步内容
    ....
} finally {
    lock.unlock(); // 必须在 finally 块中解锁,否则一旦出现异常,执行不到解锁,则一直锁住了。
}

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原语来加锁,如下:

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个线程,一个线程去写,一个线程去读。

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来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。

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吧,可中断缓存。

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做响应的修改

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()中断,并且有效处理了这个“异常”。
分享到:
评论

相关推荐

    java并发编程实践pdf笔记

    - **重进入** 一个线程在持有锁的情况下再次请求同一个锁不会被阻塞,这是Java中的`synchronized`关键字的一个特性,允许递归调用。 5. **用锁来保护状态** - 使用锁来控制对共享数据的访问是保证线程安全的一种...

    Java并发编程全景图.pdf

    Java并发编程是Java语言中最为复杂且重要的部分之一,它涉及了多线程编程、内存模型、同步机制等多个领域。为了深入理解Java并发编程,有必要了解其核心技术点和相关实现原理,以下将详细介绍文件中提及的关键知识点...

    JAVA并发编程经典书籍

    1. **并发基础**(01章):介绍并发编程的基本概念,如线程、进程、同步与异步,以及Java中的线程模型,如Java虚拟机(JVM)如何管理线程。 2. **线程的创建与生命周期**(02章):讲解如何在Java中创建线程,包括...

    java 并发编程实践

    - **同步原语**:包括`synchronized`关键字、`volatile`关键字、`Lock`接口及其实现如`ReentrantLock`,用于控制对共享资源的访问。 - **同步块与同步方法**:`synchronized`关键字用于保证线程安全,它可以修饰...

    java并发编程实践

    书中还特别强调了同步机制,如synchronized关键字、 volatile变量、Lock接口及其实现,以及ReentrantLock、ReadWriteLock等高级锁机制。理解这些同步原语对于确保线程安全和数据一致性至关重要。同时,作者也讨论了...

    基于内存模型的Java并发编程.zip

    除了上述关键字,Java还提供了其他并发工具类,如java.util.concurrent包下的Atomic类,它们提供了原语级别的原子操作,如AtomicInteger、AtomicLong等,适用于更新整数或长整型变量,而不需要使用锁。 此外,理解...

    Java并发编程实践-电子书-02章

    在Java中,可以使用`synchronized`关键字来声明方法或代码块,以确保在任何时候只有一个线程可以执行该代码段。此外,Java并发工具包(java.util.concurrent)提供了更高级的同步机制,如`ReentrantLock`、`...

    java高并发编程源码.zip

    - **同步机制**:包括`synchronized`关键字、`Lock`接口(如`ReentrantLock`)以及`Semaphore`信号量等,用于确保线程安全,防止数据不一致。 - **互斥量**:互斥量是一种同步原语,同一时刻只有一个线程能访问...

    并发编程实践,全面介绍基础知识、JVM同步原语、线程安全、低级并发工具、线程安全容器、高级线程协作工具、Executor部分等

    - **线程模型**:在Java中,线程是程序执行的最小单元,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈,但共享堆内存。 - **存储模型**:Java内存模型(JMM)定义了线程如何访问和修改共享变量,以确保正确...

    java关键字Synchronized详解

    Java中的`synchronized`关键字是实现线程同步的关键机制,它保证了在多线程环境下,对共享资源的访问是互斥的,防止了数据竞争和不一致的问题。synchronized通过锁的概念来控制对代码块或方法的访问,分为两种形式:...

    并发编程以及计算机底层原理

    7. **并发设计模式**:`17-常用并发设计模式精讲 -fox`涵盖了在并发环境中使用的经典设计模式,如生产者消费者模式、双端队列模式、读写锁模式等,这些模式有助于构建高效、稳定的并发程序。 这些知识点共同构成了...

    java自带并发框架

    Java并发框架是Java JDK中内置的一系列用于处理多线程并行执行的工具和类库,自JDK 5.0引入以来,极大地简化了并发编程的复杂性。这个框架由Doug Lea设计,并在JSR-166任务中提出,最终在Tiger(JDK 5)版本中被引入...

    简单聊聊Synchronized和ReentrantLock锁.docx

    总结来说,Synchronized和ReentrantLock是Java多线程编程中的两种重要锁机制。Synchronized以其简洁性和自动释放的特性赢得了广泛的应用,而ReentrantLock则以其灵活性和控制性在复杂场景中表现出色。理解和熟练掌握...

    并发编程demo测试包含线程池、锁、队列、信号、cas等等

    在Java中,synchronized关键字和java.util.concurrent.locks包下的Lock接口(如ReentrantLock)提供了锁支持。synchronized提供互斥访问,确保同一时间只有一个线程执行特定代码块;而Lock接口则提供了更细粒度的...

    Java-Concurrency-in-Practice.rar

    - **Java线程API**:讲解了Java中的Thread类和Runnable接口,以及如何创建和管理线程。 - **同步机制**:包括synchronized关键字、wait/notify机制,以及如何避免竞态条件和死锁。 2. **并发工具类** - **...

    Java多线程编程的线程安全性.docx

    Java中可以通过锁(Lock)和CAS(Compare-and-Swap)来实现原子性。锁提供了互斥访问,确保同一时间只有一个线程能访问共享资源,从而避免竞态条件。CAS是一种硬件级别的原语,它在处理器层面保证了对共享变量的无锁...

    JUC并发编程.rar

    Java中的并发编程主要涉及到线程、同步、锁以及高阶并发工具的使用。 在JUC中,`java.util.concurrent`包是核心,它提供了大量类和接口,如`ExecutorService`、`Future`、`Semaphore`、`CountDownLatch`、`...

    详解Java多线程编程中互斥锁ReentrantLock类的用法

    在Java多线程编程中,ReentrantLock提供了更灵活的锁管理机制,相比synchronized,它允许更细粒度的控制,并且提供了更多的同步原语。以下是一些使用ReentrantLock的关键点: 1. **可重入性**:ReentrantLock允许一...

    并发编程之一 日常学习笔记

    `synchronized`是Java中用于实现线程同步的关键字,它能确保同一时刻只有一个线程可以执行特定代码块,从而避免数据不一致性和竞态条件等问题。`synchronized`主要有两种用法:修饰方法和修饰代码块。 1. **修饰...

    Java并发编程如何降低锁粒度并实现性能优化

    还有其他并发优化策略,比如使用`volatile`关键字确保变量的可见性和避免缓存一致性问题,或者使用原子类(如`AtomicInteger`、`AtomicReference`等)来实现无锁编程。这些工具和技巧都可以帮助我们在Java并发编程中...

Global site tag (gtag.js) - Google Analytics