写在前面

Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。这两种锁以及后面提到的cas 和 volatile实际上是同步与互斥的基础。

synchronized

1. 同步一个代码块

public void func() {
synchronized (this) {
// ...
}
}

它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。

对于以下代码,使用 ExecutorService 执行了两个线程,由于调用的是同一个对象的同步代码块,因此这两个线程会进行同步,当一个线程进入同步语句块时,另一个线程就必须等待。

public class SynchronizedExample {

    public void func1() {
synchronized (this) {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
}
}
}
public static void main(String[] args) {
SynchronizedExample e1 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func1());
executorService.execute(() -> e1.func1());
}
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

对于以下代码,两个线程调用了不同对象的同步代码块,因此这两个线程就不需要同步。从输出结果可以看出,两个线程交叉执行。

public static void main(String[] args) {
SynchronizedExample e1 = new SynchronizedExample();
SynchronizedExample e2 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func1());
executorService.execute(() -> e2.func1());
}
0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

2. 同步一个方法

public synchronized void func () {
// ...
}

它和同步代码块一样,作用于同一个对象。

3. 同步一个类

public void func() {
synchronized (SynchronizedExample.class) {
// ...
}
}

作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。

public class SynchronizedExample {

    public void func2() {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
}
}
}
public static void main(String[] args) {
SynchronizedExample e1 = new SynchronizedExample();
SynchronizedExample e2 = new SynchronizedExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> e1.func2());
executorService.execute(() -> e2.func2());
}
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

4. 同步一个静态方法

public synchronized static void fun() {
// ...
}

作用于整个类。

ReentrantLock

public class LockExample {

    private Lock lock = new ReentrantLock();

    public void func() {
lock.lock();
try {
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
}
} finally {
lock.unlock(); // 确保释放锁,从而避免发生死锁。
}
}
}
public static void main(String[] args) {
LockExample lockExample = new LockExample();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> lockExample.func());
executorService.execute(() -> lockExample.func());
}
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁,相比于 synchronized,它多了以下高级功能:

1. 等待可中断

当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。

2. 可实现公平锁

公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。

synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。

3. 锁绑定多个条件

一个 ReentrantLock 对象可以同时绑定多个 Condition 对象。

synchronized 和 ReentrantLock 比较

1. 锁的实现

synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。

2. 性能

新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等。目前来看它和 ReentrantLock 的性能基本持平了,因此性能因素不再是选择 ReentrantLock 的理由。synchronized 有更大的性能优化空间,应该优先考虑 synchronized。

3. 功能

ReentrantLock 多了一些高级功能。

4. 使用选择

除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

写在最后

最后讲讲AQS,它是concurrent包实现的一个核心组件,而cas和volatile是基础中的基础,两者形成了下面四种java中的通信模式:

  • A线程写volatile变量,随后B线程读这个volatile变量。
  • A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
  • A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
  • A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。

    整个体系可以用下面这张图来体现:

## 参考
- 参考1: [Synchronized和ReentTrantLock二者区别]( https://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html)
- 参考2: [内存语义级别理解两种锁原理](http://www.infoq.com/cn/articles/java-memory-model-5)

Synchronized和ReentTrantLock二者区别的更多相关文章

  1. synchronized和ReentrantLock的区别

    synchronized和ReentrantLock的区别 synchronized是和if.else.for.while一样的关键字,ReentrantLock是类,这是二者的本质区别. 代写 既然 ...

  2. Java中synchronized和Lock的区别

    synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和 ...

  3. 详解synchronized与Lock的区别与使用

    知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...

  4. Java synchronized和 Lock 的区别与用法

    在分布式开发中,锁是线程控制的重要途径.Java为此也提供了2种锁机制,synchronized和lock.做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方.  ...

  5. 同步锁Synchronized与Lock的区别?

    synchronized与Lock两者区别: 1:Lock是一个接口,而Synchronized是关键字. 2:Synchronized会自动释放锁,而Lock必须手动释放锁. 3:Lock可以让等待 ...

  6. synchronized与volatile的区别及各自的作用、原理(学习记录)

    synchronized与volatile的区别,它们的作用及原理? 说到两者的区别,先要了解锁提供的两种特性:互斥(mutual exclusion) 和可见性(visibility). 互斥:即一 ...

  7. (转)synchronized和lock的区别

    背景:最近在准备java基础知识,对于可重入锁一直没有个清晰的认识,有必要对这块知识进行总结. 1 . 什么是可重入锁 锁的概念就不用多解释了,当某个线程A已经持有了一个锁,当线程B尝试进入被这个锁保 ...

  8. Synchronized和lock的区别和用法

    一.synchronized和lock的用法区别 (1)synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要 ...

  9. CheckStateChanged(复选框选中状态更改事件)和 CheckedChanged(单选按钮选中状态更改事件)二者区别?

    CheckStateChanged(复选框选中状态更改事件)和 CheckedChanged(单选按钮选中状态更改事件)二者区别: 复选框控件(CheckBox)提供了CheckedChanged控件 ...

随机推荐

  1. 使用 Xunit.DependencyInjection 改造测试项目

    使用 Xunit.DependencyInjection 改造测试项目 Intro 这篇文章拖了很长时间没写,之前也有介绍过 Xunit.DependencyInjection 这个项目,这个项目是由 ...

  2. 深入web workers (上)

    前段时间,为了优化某个有点复杂的功能,我采用了shared workers + indexDB,构建了一个高性能的多页面共享的服务.由于是第一次真正意义上的运用workers,比以前单纯的学习有更多体 ...

  3. 关于保存批量数据进入mysql

    提出的要求: 生成13位纯数字的卡号与8位纯数字的卡密,要求卡号与卡密都必须全表唯一,然后保存到mysql. 思路: 1.首先mysql中将这两个字段设置唯一索引,保证这两个字段的值在该表中是唯一存在 ...

  4. LDA与PCA

    参考: https://www.cnblogs.com/pinard/p/6244265.html https://blog.csdn.net/qq_25680531/article/details/ ...

  5. 国内申请苹果美区ID

    首先声明,纯粹是自己为了玩游戏而找到的方法..... 不用翻墙,在国内,就可以申请非国区的apple ID. 1.你自己申请一个自己的国区的apple ID,地址:https://appleid.ap ...

  6. Javascript的运行效率是原生代码的20%-30%

    所以jser们,写代码更仔细些吧. http://www.cnblogs.com/codemood/p/3213459.html

  7. 模块化-CommonJs、AMD、CMD、ES6

    在了解AMD,CMD规范前,还是需要先来简单地了解下什么是模块化,模块化开发?模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理.模块化是一种处理复 ...

  8. 处理ceph incompelete的经验

    前言 最近已经见到几个环境出现过incompelete了,这个在很久以前Jewel正在合入mark-complete工具的时候就有做过类似的处理,但是随着处理的环境越来越多,这个地方还是有些需要注意的 ...

  9. ios、安卓前端兼容性1

    2.input框聚焦,ios出现outline或者阴影,安卓显示正常 解决方法 input:focus{outline:none} input:{-webkit-appearance: none;} ...

  10. appium -- Xpath定位元素

    如文章<Appium基于安卓的各种FindElement的控件定位方法实践>所述,Appium拥有众多获取控件的方法.其中一种就是根据控件所在页面的XPATH来定位控件. 本文就是尝试通过 ...