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中的`synchronized`关键字的一个特性,允许递归调用。 5. **用锁来保护状态** - 使用锁来控制对共享数据的访问是保证线程安全的一种...
Java并发编程是Java语言中最为复杂且重要的部分之一,它涉及了多线程编程、内存模型、同步机制等多个领域。为了深入理解Java并发编程,有必要了解其核心技术点和相关实现原理,以下将详细介绍文件中提及的关键知识点...
1. **并发基础**(01章):介绍并发编程的基本概念,如线程、进程、同步与异步,以及Java中的线程模型,如Java虚拟机(JVM)如何管理线程。 2. **线程的创建与生命周期**(02章):讲解如何在Java中创建线程,包括...
- **同步原语**:包括`synchronized`关键字、`volatile`关键字、`Lock`接口及其实现如`ReentrantLock`,用于控制对共享资源的访问。 - **同步块与同步方法**:`synchronized`关键字用于保证线程安全,它可以修饰...
书中还特别强调了同步机制,如synchronized关键字、 volatile变量、Lock接口及其实现,以及ReentrantLock、ReadWriteLock等高级锁机制。理解这些同步原语对于确保线程安全和数据一致性至关重要。同时,作者也讨论了...
除了上述关键字,Java还提供了其他并发工具类,如java.util.concurrent包下的Atomic类,它们提供了原语级别的原子操作,如AtomicInteger、AtomicLong等,适用于更新整数或长整型变量,而不需要使用锁。 此外,理解...
在Java中,可以使用`synchronized`关键字来声明方法或代码块,以确保在任何时候只有一个线程可以执行该代码段。此外,Java并发工具包(java.util.concurrent)提供了更高级的同步机制,如`ReentrantLock`、`...
- **同步机制**:包括`synchronized`关键字、`Lock`接口(如`ReentrantLock`)以及`Semaphore`信号量等,用于确保线程安全,防止数据不一致。 - **互斥量**:互斥量是一种同步原语,同一时刻只有一个线程能访问...
- **线程模型**:在Java中,线程是程序执行的最小单元,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈,但共享堆内存。 - **存储模型**:Java内存模型(JMM)定义了线程如何访问和修改共享变量,以确保正确...
Java中的`synchronized`关键字是实现线程同步的关键机制,它保证了在多线程环境下,对共享资源的访问是互斥的,防止了数据竞争和不一致的问题。synchronized通过锁的概念来控制对代码块或方法的访问,分为两种形式:...
7. **并发设计模式**:`17-常用并发设计模式精讲 -fox`涵盖了在并发环境中使用的经典设计模式,如生产者消费者模式、双端队列模式、读写锁模式等,这些模式有助于构建高效、稳定的并发程序。 这些知识点共同构成了...
Java并发框架是Java JDK中内置的一系列用于处理多线程并行执行的工具和类库,自JDK 5.0引入以来,极大地简化了并发编程的复杂性。这个框架由Doug Lea设计,并在JSR-166任务中提出,最终在Tiger(JDK 5)版本中被引入...
总结来说,Synchronized和ReentrantLock是Java多线程编程中的两种重要锁机制。Synchronized以其简洁性和自动释放的特性赢得了广泛的应用,而ReentrantLock则以其灵活性和控制性在复杂场景中表现出色。理解和熟练掌握...
在Java中,synchronized关键字和java.util.concurrent.locks包下的Lock接口(如ReentrantLock)提供了锁支持。synchronized提供互斥访问,确保同一时间只有一个线程执行特定代码块;而Lock接口则提供了更细粒度的...
- **Java线程API**:讲解了Java中的Thread类和Runnable接口,以及如何创建和管理线程。 - **同步机制**:包括synchronized关键字、wait/notify机制,以及如何避免竞态条件和死锁。 2. **并发工具类** - **...
Java中可以通过锁(Lock)和CAS(Compare-and-Swap)来实现原子性。锁提供了互斥访问,确保同一时间只有一个线程能访问共享资源,从而避免竞态条件。CAS是一种硬件级别的原语,它在处理器层面保证了对共享变量的无锁...
Java中的并发编程主要涉及到线程、同步、锁以及高阶并发工具的使用。 在JUC中,`java.util.concurrent`包是核心,它提供了大量类和接口,如`ExecutorService`、`Future`、`Semaphore`、`CountDownLatch`、`...
在Java多线程编程中,ReentrantLock提供了更灵活的锁管理机制,相比synchronized,它允许更细粒度的控制,并且提供了更多的同步原语。以下是一些使用ReentrantLock的关键点: 1. **可重入性**:ReentrantLock允许一...
`synchronized`是Java中用于实现线程同步的关键字,它能确保同一时刻只有一个线程可以执行特定代码块,从而避免数据不一致性和竞态条件等问题。`synchronized`主要有两种用法:修饰方法和修饰代码块。 1. **修饰...
还有其他并发优化策略,比如使用`volatile`关键字确保变量的可见性和避免缓存一致性问题,或者使用原子类(如`AtomicInteger`、`AtomicReference`等)来实现无锁编程。这些工具和技巧都可以帮助我们在Java并发编程中...