Synchronized 

同步代码块

  使用 monitorenter 和 moniterexit 指令实现, monitorenter指令插入到同步代码块的开始位置, moniterexit 指令插入到同步代码块的结束位置, jvm 需要保证每一个 monitorenter 都有一个 moniterexit 与之对应。

  任何对象都有一个 monitor 与之相关联,当且一个 monitor 被持有之后,他将处于锁定状态。

  当执行 monitorenter 指令时,当前线程将试图获取对象锁所对应的 monitor 的持有权,当对象锁的 monitor 的进入计数器为 0,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。

  如果当前线程已经拥有对象锁的 monitor 的持有权,那它可以重入这个 monitor,重入时计数器的值会加 1。

  倘若其他线程已经拥有对象锁的 monitor 的所有权,那当前线程将被阻塞,直到正在执行的线程执行完毕,即 monitorexit 指令被执行,执行线程将释放 monitor 并设置计数器值为 0,其他线程将有机会持有 monitor。

  为了保证在方法异常完成时 monitorenter 和 monitorexit 指令依然可以正确配对执行,编译器会自动产生一个异常处理器,这个异常处理器可处理所有的异常,它的目的就是用来执行 monitorexit 指令。

  从字节码中也可以看出多了一个 monitorexit 指令。

 

同步方法

  synchronized 方法会被翻译成普通的方法调用。在 JVM 字节码层面并没有任何特别的指令来实现被 synchronized 修饰的方法。

  在 Class 文件的方法表中将该方法的 access_flags 字段中的 synchronized 标志位置 1,表示该方法是同步方法并使用调用该方法的对象(对象锁)或该方法所属的 Class(类锁) 做为锁对象。

Lock

ReentrantLock:

  使用lockInterruptibly()获得锁,可以响应中断

  使用tryLock()尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

  tryLock(long,TimeUnit):与tryLock类似,只不过是有等待时间,在等待时间内获取到锁返回true,超时返回false。

  类ReentrantLock实现等待/同步功能,需要借助于condition对象。

  Condition类具有很好的灵活性,可以实现多路通知功能:在一个Lock对象中创建多个Condition(对象监视器)实例,线程对象可以注册在指定的Condition中,从而有选择性地进行线程通知,在调度线程上更灵活。

  在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的,但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”。

 volatile boolean isProcess = false;
ReentrantLock lock  = new ReentrantLock();
Condtion processReady = lock.newCondtion();
thread: run() {
    lock.lock();
    isProcess = true;
    try {
    while(!isProcessReady) {  //isProcessReady 是另外一个线程的控制变量
       processReady.await();//释放了lock,在此等待signal
     }catch (InterruptedException e) {
          Thread.currentThread().interrupt();
        } finally {
          lock.unlock();
          isProcess = false;
        }
      }
    }
}

ReentrantReadWriteLock:

  在没有线程进行写操作时,多个进行读操作的线程都可以获取读锁,而进行写入操作的Thread只有在获取写锁后才能进行写入操作。

  多个线程可以同时进行读操作,但同一时刻只允许一个Thread进行写入操作。 

  lock.readLock().lock():获得读锁

  lock.writeLock().lock():获得写锁

公平锁与非公平锁:

  公平锁:标识线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。

  非公平锁(默认):一种线程抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方法可能会导致某些线程一直拿不到锁,自然不公平。

  synchronized是非公平锁,它无法保证等待的线程获取锁的顺序。

  对于ReentrantLockReentrantReadWriteLock,默认情况下是非公平锁,但是可以设置为公平锁。

synchronized和lock的区别

  Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  Lock可以有公平锁和非公平锁,默认非公平锁,synchronized只有非公平锁。

  Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  Lock可以提高多个线程进行读操作的效率。(读写锁)

synchronized是悲观锁,Lock是乐观锁

  synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。

  独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。

  而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。

  对于线程的阻塞或唤醒,需要用户态和核心态切换,状态切换需要很多处理器时间。

  而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

  乐观锁实现的机制就是CAS操作(Compare and Swap)。

 JDK 1.6 后对 synchronized 的锁优化:

  自旋锁与自适应自旋:线程挂起和恢复的操作都需要转入内核态中完成,这些操作给系统的并发性能带来了很大的压力,在许多应用中,共享数据的锁定状态只会持续很短的一段时间,为了这段时间去挂起和恢复线程并不值得,可以让后请求锁的线程等待一会儿,但不放弃处理器的执行时间,让线程执行一个忙循环(自旋)。

自旋锁默认的自旋次数值是10次,可以使用参数-XX:PreBlockSpin更改。

自适应自旋意味着自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

锁消除:虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。锁消除的主要判定依据来源于逃逸分析的数据支持。

锁粗化:如果虚拟机探测到有一系列连续操作都对同一个对象反复加锁和解锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。

轻量级锁:使用对象头的Mark Word中锁标志位代替操作系统互斥量实现的锁。轻量级锁并不是用来代替重量级锁,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

轻量级锁是在无竞争的情况下使用CAS(Compare-and-Swap)操作去消除同步使用的互斥量。

偏向锁:和轻量级锁原理基本一致,但偏向锁在无竞争的情况下把整个同步都消除掉,连CAS操作都不做了。

java中每个对象都可作为锁,锁有四种级别:

  按照量级从轻到重分为:无锁、偏向锁、轻量级锁、重量级锁。

  每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。

java对象头:

偏向锁:

  大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。

  当一个线程访问同步块并获取锁时,会在对象头记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,而只需测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

    如果测试成功,表示线程已经获得了锁,

    如果测试失败,则需要再测试下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁),

      如果对象的偏向锁标志位为0(当前不是偏向锁),说明发生了竞争,已经膨胀为轻量级锁。这时使用CAS操作尝试获得锁。

      如果为1,说明还是偏向锁不过请求的线程不是原来那个了。这时只需要使用CAS尝试把对象头偏向锁从原来那个线程指向目前求锁的线程。

轻量级锁:

  线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。

  然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

  轻量级解锁时,会使用原子的CAS操作来将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁

synchronized和lock的更多相关文章

  1. 线程安全、数据同步之 synchronized 与 Lock

    本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要 ...

  2. 【转载】synchronized 与 Lock 的那点事

    最近在做一个监控系统,该系统主要包括对数据实时分析和存储两个部分,由于并发量比较高,所以不可避免的使用到了一些并发的知识.为了实现这些要求,后台使用一个队列作为缓存,对于请求只管往缓存里写数据.同时启 ...

  3. 转:synchronized和LOCK的实现原理---深入JVM锁机制

    JVM底层又是如何实现synchronized的? 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug ...

  4. synchronized 与 Lock 的那点事

    最近在做一个监控系统,该系统主要包括对数据实时分析和存储两个部分,由于并发量比较高,所以不可避免的使用到了一些并发的知识.为了实现这些要求,后台使用一个队列作为缓存,对于请求只管往缓存里写数据.同时启 ...

  5. JAVA中synchronized和lock详解

         目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronize ...

  6. Java多线程编程(四)—浅谈synchronized与lock

    一.共享资源竞争问题 在Java语言的并发编程中,由于我们不知道线程实际上在何时运行,所以在实际多线程编程中,如果两个线程访问相同的资源,那么由于线程运行的不确定性便会在这种多线程中产生访问错误.所以 ...

  7. synchronized和lock比对

    前言:在上面的博客说了synchronized的一些用法,下面我们再来看看lock,这个出现频率也是非常高的一个. 1:获取Lock锁的几种方式 前面说了synchronized有锁对象和锁类对象,当 ...

  8. Java中synchronized和Lock的区别

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

  9. 【Java】synchronized与lock的区别

    从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...

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

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

随机推荐

  1. Exploring Pyramids UVALive - 3516 (记忆化DP)

    题意:给定一个序列 问有多少棵树与之对应 题目连接:https://cn.vjudge.net/problem/UVALive-3516 对于这一序列  分两种2情况 当前分支 和 其它分支  用df ...

  2. nginx mp3

    location /mp3 { alias "d:/mp3"; default_type audio/mpeg; autoindex on; }

  3. 洛谷P4338 [ZJOI2018]历史(LCT,树形DP,树链剖分)

    洛谷题目传送门 ZJOI的考场上最弱外省选手T2 10分成功滚粗...... 首先要想到30分的结论 说实话Day1前几天刚刚刚掉了SDOI2017的树点涂色,考场上也想到了这一点 想到了又有什么用? ...

  4. ssm框架配置过程

    1.pom.xml配置 1.1<build>标签中配置<plugins>和<resources>,即插件和资源文件 1.2 <properties>标签 ...

  5. Atcoder Grand 006 C-Rabbit Exercise

    题意: 数轴上有n只兔子,第i只兔子的坐标为xi. 有一组操作,这组操作的第i个操作是要让第ai只兔子等概率的跳到自己关于第ai+1或第ai-1只兔子的对称点. 进行K组操作,求每只兔子最后坐标的期望 ...

  6. django 学习笔记(转)

    原文链接:https://my.oschina.net/linktime/blog/105280 例如有一下模型 from django.db import models class person(m ...

  7. OpenStack 网络服务 Neutron 私有网络构建(十九)

    本章内容基于之前提供者网络构建的基础上进行改动,之前文章参考如下: Openstack 网络服务 Neutron介绍和控制节点部署 (九) Openstack 网络服务 Neutron计算节点部署(十 ...

  8. bzoj千题计划254:bzoj2286: [Sdoi2011]消耗战

    http://www.lydsy.com/JudgeOnline/problem.php?id=2286 虚树上树形DP #include<cmath> #include<cstdi ...

  9. 贪心算法: Codevs 1052 地鼠游戏

    #include <iostream> #include <algorithm> #include <queue> #include <cstring> ...

  10. Docker CE的安装 与镜像加速

    Docker CE 的安装与镜像加速 Docker CE是docker的开源版本 CENTOS 安装Docker CE 系统要求: 操作系统需要使用centos7() centos-extras库 必 ...