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

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

  1. Java并发编程中的设计模式解析(二)一个单例的七种写法

    Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...

  2. Java并发编程中的若干核心技术,向高手进阶!

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  3. Java并发编程中的相关注解

    引自:http://www.cnblogs.com/phoebus0501/archive/2011/02/21/1960077.html Java并发编程中,用到了一些专门为并发编程准备的 Anno ...

  4. Java并发编程知识点总结Volatile、Synchronized、Lock实现原理

    Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...

  5. Java并发编程中的设计模式解析(一)

    Java并发编程,除了被用于各种Web应用.分布式系统和大数据系统,构成高并发系统的核心基础外,其本身也蕴含着大量的设计模式思想在里面.这一系列文章主要是结合Java源码,对并发编程中使用到的.实现的 ...

  6. 【Java并发编程之深入理解】Synchronized的使用

    原文:https://blog.csdn.net/zjy15203167987/article/details/82531772 1.为什么要使用synchronized 在并发编程中存在线程安全问题 ...

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

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17199201     在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访 ...

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

    在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确 ...

  9. Java并发编程实战3-可见性与volatile关键字

    1. 缓存一致性问题 在计算机中,每条指令都是在CPU执行的,而CPU又不具备存储数据的功能,因此数据都是存储在主存(即内存)和外存(硬盘)中.但是,主存中数据的存取速度高于外存中数据的存取速度(这也 ...

随机推荐

  1. vue cli的安装与使用

    一.简介 vue作为前端开发的热门工具,受到前端开发人员的喜爱.为了简化项目的构建,采用vue cli来简化开发. vue cli是一个全局安装的npm包,提供了终端使用的命令.vue create可 ...

  2. k8s 如何 Failover?

    上一节我们有 3 个 nginx 副本分别运行在 k8s-node1 和 k8s-node2 上.现在模拟 k8s-node2 故障,关闭该节点. 等待一段时间,Kubernetes 会检查到 k8s ...

  3. C++基础:虚函数、重载、覆盖、隐藏<转>

    转自:http://www.2cto.com/kf/201404/291772.html 虚函数总是跟多态联系在一起,引入虚函数可以使用基类指针对继承类对象进行操作! 虚函数:继承接口(函数名,参数, ...

  4. myeclipse 导入项目时no projects are found to import解决办法

    myeclipse 识别一个工程需要.classpath与.project文件,一般无需提交SVN所以项目切下来的时候是没有这两个文件的. 方法1: 1) 在myeclipse中新建一个和你要导入的项 ...

  5. XML解析(一) DOM解析

    XML解析技术主要有三种: (1)DOM(Document Object Model)文档对象模型:是 W3C 组织推荐的解析XML 的一种方式,即官方的XML解析技术. (2)SAX(Simple ...

  6. hihoCode-1043-完全背包

    我们定义:best(i,x)代表i件以前的物品已经决定好选择多少件,并且在剩余奖券x的情况下的最优解. 我们可以考虑最后一步,是否再次选择i物品,在不超过持有奖券总额的情况下.上面的第二个式子的k是大 ...

  7. 简单的Redis数据迁移

    dump迁移 1.安装redis-dump工具 sudo apt-get install ruby rubygems ruby-devel -y gem sources --add http://ge ...

  8. MySQL 查询优化之 Index Condition Pushdown

    MySQL 查询优化之 Index Condition Pushdown Index Condition Pushdown限制条件 Index Condition Pushdown工作原理 ICP的开 ...

  9. 刚毕业去面试Python工程师,这几道题太难了,Python面试题No11

    写在前面 本想停一段时间这个系列,但是好多朋友给我发信息说让我继续整理下去,so,继续吧~ 第1题: docstring是什么? docstring是一种文档字符串,用于解释构造的作用.我们在函数.类 ...

  10. python--管道, 事件, 信号量, 进程池

    一 . 管道 (了解) from multiprocessing import Process, Pipe def f1(conn): # 管道的recv 里面不用写数字 from_main_proc ...