本文是讲述ReentrantLock类与synchronized关键字同时使用的问题,不是ReentrantLock类与synchronized关键字的教程。
synchronized关键字作为java多线程编程中非常重要的关键字之一,它维护这线程并发中的安全。通常使用synchronized有2种方式。
锁定当前实例
//通过方法上使用synchronized达到锁定效果
public synchronized void xxx() {
//...
}
//通过锁定指定的实例达到锁定效果
public void yyy(){
synchronized (this) {
//...
}
}
public void zzz(){
synchronized (xObject) {
//...
}
}
其中第一种和第二种都是对当前方法属于的对象实例的琐定,而第三种为锁定指定的实例。
本文不打算详细讲解synchronized关键字,有关synchronized的详细说明请参考其他资料。
java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。ReentrantLock作为Lock接口的实现,定义了可重入锁。根据API的说明:“一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。”可以发现,ReentrantLock的最基本的作用就是实现了使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义。
ReentrantLock提供的方法比较多,但这里我们只讨论实现synchronized相同功能的方式。
API中所使用的示例为:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
可以看出,如果上面代码换成synchronized的话,应该是:
public synchronized void m() {
// ... method body
}
或者
public void m() {
synchronized (this) {
// ... method body
}
}
锁定的是当前的实例。这也是本文的重点。(关于ReentrantLock的更多信息,请参考其他资料)
既然ReentrantLock和synchronized都提供了相同的行为(这里不讨论性能问题),那么在使用过程中,对于线程并发编程,使用ReentrantLock与synchronized都是可以的,他们也都可以工作得很好。但是,如果同时使用它们两个呢?结果又会是怎么样呢?
看如下代码:
package cn.agrael.test.thread;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockAndSynchronized {
private final ReentrantLock lock = new ReentrantLock();
private volatile int i = 0;
public void lockAdd() throws Exception {
lock.lock();
try {
check("lockAdd");
i++;
i++;
} finally {
lock.unlock();
}
}
public synchronized void synchronizedAdd() throws Exception {
check("synchronizedAdd");
i++;
i++;
}
// public void synchronizedAdd() throws Exception {
// lock.lock();
// try {
// check("lockAdd");
// i++;
// i++;
// } finally {
// lock.unlock();
// }
// }
private void check(String methodName) throws Exception {
if (i % 2 != 0) {
throw new Exception(methodName + " : " + i);
}
}
public static void main(String[] args) throws Exception {
final ReentrantLockAndSynchronized add = new ReentrantLockAndSynchronized();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
while (true) {
add.lockAdd();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
try {
while (true) {
add.synchronizedAdd();
}
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
});
thread1.start();
thread2.start();
}
}
其中有个int型的i变量,并提供了使用ReentrantLock锁定的lockAdd方法与使用synchronized锁定的synchronizedAdd方法,这2个方法都提供相同的操作,先验证i是否为偶数,如果不是则抛出异常,并且提供2次i++的操作。java中的i++并非原子性的操作,会涉及读和写,再者提供2次i++,如果是这样的话,会出现并发问题,所以我们提供了ReentrantLock以及synchronized来锁定,保证线程安全。如果我们的想法可行,那么i永远被读到的结果都是偶数,也就不永远不会抛我们所指定的异常。但是结果却不是这样,运行一会后,就抛出了异常,证明我们的想法失败了。因为ReentrantLock与synchronized所提供的机制不同,导致了他们是相对独立的,相当于是两把锁,各自锁定各自的。
所以最后我们下的结论就是不要同时使用ReentrantLock类与synchronized关键字锁定会修改同一个资源的不同方法。
分享到:
相关推荐
Lock接口与synchronized关键字在Java并发编程中都是用于实现同步机制的重要工具,但它们在使用方式、功能特性以及灵活性等方面存在一些显著的差异。 首先,从使用方式上来看,synchronized是Java语言内置的关键字,...
java语言 并发编程 ReentrantLock与synchronized区别 详解
java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从使用的角度来分析...
助于理解的例子 博文链接:https://uule.iteye.com/blog/1488356
比较 Locks 框架与传统 synchronized 关键字的不同之处。 ReentrantLock 简介: 详细讲解 ReentrantLock 的概念和特点。解释为什么它被称为“可重入锁”,以及如何解决传统锁可能的问题。 ReentrantLock 的基本用法...
深入java并发编程,使用ReentrantLock和 Synchronized加锁
synchronized关键字编译后会在同步块前后形成monitorenter和monitorexit两个字节码指令 执行monitorenter指令时需要先获得对象的锁(每个对象有一个监视器锁monitor),如果这个对象没被锁或者当前线程已经获得...
java lock synchronized
可重入锁: 也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,...ReentrantLock 在Java也是一个基础的锁,ReentrantLock 实现Lock接口提供一系列的基础函数,开发人员可以灵活的是应用函数满足各种复杂多变应用场景;
比较 Lock 接口与传统 synchronized 关键字的不同之处。 常用 Lock 接口实现类: 详细讲解 Lock 接口的一些常用实现类,如 ReentrantLock、ReadWriteLock、StampedLock 等。解释它们的特点和适用场景。 Lock 接口的...
在 Java 5 以前,synchronized 是仅有的同步手段,在代码中, synchronized 可以用来修饰方法,也可以使用在特定的代码块儿上,本质上
通过将 MyRunnable 对象传递给 Thread 类的构造方法,我们创建了一个新的线程,并将 run 方法作为线程的执行逻辑。 除了基本的线程创建和启动,Java还提供了一些管理线程的方法和工具,例如: sleep 方法:使当前...
ReentrantLock的使用及注意事项
ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重...
synchronized关键字”锁”的实现本质 synchronized关键字实现的锁是依赖于JVM的,底层调用的是操作系统的指令集实现。 Lock接口实现的锁不一样,例如ReentrantLock锁是基于JDK实现的,有Java原生代码来实现的。 ...
使用ReentrantLock和Lambda表达式让同步更纯净Java开发Java经验技巧共5页.pdf.zip
目录: 简介 ReentrantLock的基本用法 2.1 创建ReentrantLock 2.2 获取锁和释放锁 公平性与非公平性 3.1 公平锁 3.2 非公平锁 中断响应 ...ReentrantLock与synchronized的对比 最佳实践与注意事项
一个condition对象的signal(signalAll)方法和该对象的await方法是一一对应的,也就是一个condition对象的signal(signalAll)方法不能唤醒其他condition对象的await方法 ReentrantLock类可以唤醒指定条件的...
看完《think in java》多线程章节,自己写的多线程文档,还结合了其他的相关网络资料。 线程 一....1)为什么要使用线程池 2 2)一个具有线程池的工作队列 3 ...1)ReentrantLock和synchronized关键字的区别 41