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("读结束");    
     
    }    
     
}    
     
    
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()中断,并且有效处理了这个“异常”。


获取【下载地址】   QQ: 313596790   【免费支持更新】
A 代码生成器(开发利器);全部是源码  
   增删改查的处理类,service层,mybatis的xml,SQL( mysql   和oracle)脚本,   jsp页面 都生成
   就不用写搬砖的代码了,生成的放到项目里,可以直接运行
B 阿里巴巴数据库连接池druid;
  数据库连接池  阿里巴巴的 druid。Druid在监控、可扩展性、稳定性和性能方面都有明显的优势
C 安全权限框架shiro ;
  Shiro 是一个用 Java 语言实现的框架,通过一个简单易用的 API 提供身份验证和授权,更安全,更可靠
D ehcache 分布式缓存;
  是一个纯Java的进程内缓存框架,具有快速、精干等特点,广泛使用的开源Java分布式缓存

时间: 2025-01-01 20:44:55

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

JAVA并发编程3_线程同步之synchronized关键字

在上一篇博客里讲解了JAVA的线程的内存模型,见:JAVA并发编程2_线程安全&内存模型,接着上一篇提到的问题解决多线程共享资源的情况下的线程安全问题. 不安全线程分析 public class Test implements Runnable { private int i = 0; private int getNext() { return i++; } @Override public void run() { // synchronized while (true) { synchro

java并发编程中的ReentrantLock(一)

package com.wangan.logistic; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class WanganReentrantLockService { private Lock lock = new ReentrantLock(); priv

JAVA并发编程4_线程同步之volatile关键字

上一篇博客JAVA并发编程3_线程同步之synchronized关键字中讲解了JAVA中保证线程同步的关键字synchronized,其实JAVA里面还有个较弱的同步机制volatile.volatile关键字是JAVA中的轻量级的同步机制,用来将变量的更新操作同步到其他线程.从内存可见性的角度来说,写入volatile变量相当于退出同步代码块,读取volatile变量相当于进入同步代码块. 旧的内存模型:保证读写volatile都直接发生在main memory中. 在新的内存模型下(1.5)

java并发编程中CountDownLatch和CyclicBarrier的使用

转自:http://blog.csdn.net/hbzyaxiu520/article/details/6183714 在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景,遇到这样的场景应该如何解决? 如果是一个线程等待一个线程,则可以通过await()和notify()来实现: 如果是一个线程等待多个线程,则就可以使用CountDownLatch和CyclicBarrier来实现比较好的控制. 下面来详细描述下CountDownLatch的应用场景: 例如:百米赛跑:8名运动员同时

【Java并发编程】之七:使用synchronized获取互斥锁的几点说明

 在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确保在某一时刻,方法内只允许有一个线程. 采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁.每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池.任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,

转:【Java并发编程】之七:使用synchronized获取互斥锁的几点说明

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17199201     在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确保在某一时刻,方法内只允许有一个线程. 采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁.每个对象都有一个monitor(锁标记)

java并发编程中常用的工具类 Executor

/***************************************************  * TODO: description .  * @author: gao_chun  * @since:  2015-4-17  * @version: 1.0.0  * @remark: 转载请注明出处  **************************************************/ java.util.concurrent.Executor 使用 Execut

Java并发编程(六):volatile关键字解析(转载)

原文转载自:http://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我

Java并发编程1——线程状态、synchronized

以下内容主要总结自<Java多线程编程核心技术>,不定时补充更新. 一.线程的状态 Java中,线程的状态有以下6类:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED.各状态之间的关系可用下图表示: 二.常用方法介绍 1.thread.start()和thread.run()的区别 1 public static void main(String[] args) { 2 Thread t = new Thread(); 3 t