一句话总结synchronized:

JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断的性质。

一.synchronized的作用

使用synchronized修饰方法或者代码块时,它能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。

二.synchronized的地位

1.Synchronized是Java的关键字,被Java语言原生支持

2.是最基本的互斥同步手段

3.是并发编程中的元老级角色,是并发编程的主要内容

三.synchronized的性质

synchronized的具有可重入性和不可中断性

1.可重入性(synchronized区别于其他锁的一个很重要的性质)

什么是可重入:指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。也叫做递归锁。Java中两大递归锁:Synchronized和ReentrantLock。

锁的不可重入性:线程拿到一把锁了,如果想再次使用这把锁,必须先将锁释放,与其他线程再次进行竞争。

锁的可重入性:如果线程已经拿到了锁,试图去请求这个已经获得到的锁,而无需提前释放,直接就可以使用手里的这把锁,这就叫做可重入性

synchronized是可重入锁

可重入锁的好处:

1 避免死锁

2 提升封装性

粒度:

可重入的特性是线程级别的,不是调用级别的。

情况1:证明同一个方法是可重入的

实例代码:

public class SynchronizedRecursion {

int a = 0;

public static void main(String[] args) {

SynchronizedRecursion sr = new SynchronizedRecursion();

sr.method1();

}

private synchronized void method1(){

System.out.println(synchronized修饰的方法,a = + a);

if(a==0){

a++;

method1();

}

}

}

情况2:证明可重入不要求是同一个方法

实例代码:

public class SynchronizedOtherMethod {

public synchronized void method1(){

System.out.println(我是method1);

method2();

}

public synchronized void method2(){

System.out.println(我是method2);

}

public static void main(String[] args) {

SynchronizedOtherMethod som = new SynchronizedOtherMethod();

som.method1();

}

}

情况3:证明可重入不要求是同一个类中的

实例代码:

public class SynchronizedSuperClass {

public synchronized void doSomething(){

System.out.println(我是父类方法);

}

}

class TestClass extends SynchronizedSuperClass{

public synchronized void doSomething(){

System.out.println(我是子类方法);

super.doSomething();

}

public static void main(String[] args) {

TestClass tc = new TestClass();

tc.doSomething();

}

}

2.不可中断(相比于其他有的锁可以中断,这个性质是synchronized的一个劣势所在)

一旦这个锁已经被别的线程获得了,如果当前线程还想获得,只能选择等待或者阻塞,直到别的线程释放这个锁。如果别的线程 永远不释放锁,那么线程只能永远地等下去。

相比之下,Lock类,可以拥有中断的能力。

第一点,如果我觉得我等的时候太长了,有权中断现在已经获取到锁的线程的执行; 第二点,如果我觉得我等待的时间太长了不想再等了,也可以退出。

Lock比synchronized灵活很多,但是编码易出错。

四.synchronized的原理

1.加解锁原理:现象,时机,深入JVM看字节码

现象:每一个类的实例对应一把锁,而每一个synchronized方法都必须先获得调用该方法的类的实例的锁方能执行,否则线程会阻塞。而方法一旦执行,它就独占了这把锁,直到该方法返回或者是抛出异常,才将锁释放。一旦锁释放之后,之前被阻塞的线程才能获得这把锁,从被阻塞的状态重新进入到可执行的状态。当一个对象中有synchronized修饰的方法或者代码块的时候,要想执行这段代码,就必须先获得这个对象锁,如果此对象的对象锁已经被其他调用者所占用,就必须等待它被释放。所有的Java对象都含有一个互斥锁,这个锁由JVM自动去获取和释放,我们只需要指定这个对象就行了,至于锁的释放和获取不需要我们操心。

获取和释放锁的时机:内置锁

我们知道每一个Java对象都可以用作一个同步的锁,这个锁叫做内部锁,或者叫做监视器锁--monitor lock。线程在进入到同步代码块之前,会自动获得这个锁,并且在退出同步代码块的时候会自动的释放这个锁,无论是正常途径退出还是抛出异常退出。获得这个内置锁的唯一途径就是进入这个锁保护的同步代码块或者同步方法中。

实例代码:

public class SynchronizedToLock {

Lock lock = new ReentrantLock();

public synchronized void method1(){

System.out.println(我是synchronized锁);

}

public void method2(){

lock.lock(); //加锁

try{

System.out.println(我是lock锁);

}finally {

lock.unlock(); //释放锁

}

}

public static void main(String[] args) {

SynchronizedToLock stl = new SynchronizedToLock();

stl.method1();

stl.method2();

}

}

method1()和method2()方法等价

深入JVM看字节码:

实例代码:

public class Decompilation {

private Object object = new Object();

//同步代码块

public void insert(Thread thread){

synchronized (object){

}

}

}

1、将上面的Java代码编译为 .class文件,命令窗口执行命令:javac xxx.java 进行编译。

2、通过反编译查看字节码,执行命令:javap -verbose xxx.class 进行反编译

3、synchronized如何实现的,有个加锁monitorenter和解锁monitorexit

读到该指令,会让monitor计数器+1或-1

具体如图:

线程既可以在方法完成之后退出,也可以在抛出异常后退出,因此monitorexit数量多于monitorenter。

monitorenter和monitorexit指令

monitorenter:线程每次进入时,计数器+1。如果重入,继续加

monitorexit:线程退出时,计数器-1.变为0时候,其他线程可以获取锁。

2.可重入原理:加锁次数计数器

1、JVM负责跟踪对象被加锁的次数;

2、有个monitor计数器,线程第一次给对象加锁的时候,计数变为1.每当这个相同的线程在此对象上再次获得锁时,计数会递增。

3、任务结束离开,则会执行monitorexit,计数递减,直到完全释放

3.可见性原理:Java内存模型

五.synchronized的缺陷

1.效率低:

· 锁的释放情况少

· 试图获得锁时不能设定超时

· 不能中断一个正在试图获得锁的线程

2.不够灵活(读写锁更灵活:读操作的时候不会加锁,写操作的时候才会加锁):

· 加锁和释放的时机单一

· 每个锁仅有单一的条件(某个对象),可能是不够的

3.无法知道是否成功获取到锁,而使用Lock则不同。

Lock可以,如果尝试成功了做一些逻辑判断,如果没有成功做另外一些逻辑判断.

Lock类:

lock.lock();lock.unlock();通过这两个方法,可以手动加锁和解锁。

lock.tryLock();lock.tryLock(10, TimeUnit.MINUTES);可以判断是否加锁,以及设置超时时间,返回类型为boolean

六.synchronized的用法

1、对象锁:

a、方法锁(默认锁对象为this当前实例对象):synchronized修饰普通方法,锁对象默认为this

实例代码:

public synchronized void run(){

for(int j = 0; j 100000; j++){

i++;

}

}

b、同步代码块锁: 自己手动指定锁对象

实例代码:

public synchronized void run(){

synchronized (this){

for(int j = 0; j 100000; j++){

i++;

}

}

}

2、类锁:

Java类可能有很多个对象,但只要一个class对象。类锁只不过是Class对象的锁而已。类锁只能在同一时刻被一个对象拥有。

a、使用关键字synchronized修饰静态static的方法

实例代码:

public void run(){

method();

}

public static synchronized void method(){

for(int j = 0; j 100000; j++){

i++;

}

}

b、使用关键字synchronized指定锁为Class对象: synchronized(*.class)

实例代码:

public synchronized void run(){

synchronized (DisappearRequest2.class){

for(int j = 0; j 100000; j++){

i++;

}

}

}

synchronized使用的注意点:

锁的信息是保存在对象头中的、作用域不易过大,影响性能、避免死锁

七.不使用并发情况会带来的后果以及解决办法

问题:两个线程同时a++,最后结果会比预计的少

原因

count++,它看上去只是一个操作,实际上包含了三个动作:

1.读取count

2.将count加一

3.将count的值写入到内存中

这三个操作,如果不按照原子去执行,就会带来并发问题

解决之前:

public class DisappearRequest1 implements Runnable{

static DisappearRequest1 instance = new DisappearRequest1();

static int i = 0;

@Override

public void run(){

for(int j = 0; j 100000; j++){

i++;

}

}

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join(); //等待线程结束

t2.join();

System.out.println(i= + i);

}

}

解决:

public class DisappearRequest2 implements Runnable{

static DisappearRequest2 instance = new DisappearRequest2();

static int i = 0;

//方法1:直接把synchronized加到方法上

/* public synchronized void run(){

for(int j = 0; j 100000; j++){

i++;

}

}*/

//方法2:对象锁的同步代码块

/*public synchronized void run(){

synchronized (this){

for(int j = 0; j 100000; j++){

i++;

}

}

}*/

//第三种:类锁:synchronized(*.class)

public synchronized void run(){

synchronized (DisappearRequest2.class){

for(int j = 0; j 100000; j++){

i++;

}

}

}

//第四种:类锁

/*public void run(){

method();

}

public static synchronized void method(){

for(int j = 0; j 100000; j++){

i++;

}

}*/

public static void main(String[] args) throws InterruptedException {

Thread t1 = new Thread(instance);

Thread t2 = new Thread(instance);

t1.start();

t2.start();

t1.join(); //等待线程结束

t2.join();

System.out.println(i= + i);

}

}

八.关于多线程访问同步方法的七种情况的总结

1.两个线程同时访问一个对象的同步方法(对象锁):串行执行

这种情况就是对象锁的方法锁情况。会相互等待,只能有一个线程持有锁。

2.两个线程访问的是两个对象的同步方法:两个锁对象互不干扰,并行执行

不会加锁,因为访问的是不同的实例

3.两个线程访问的是synchronized的静态方法:两个锁对象是同一个锁,串行执行

这种情况就是类锁的静态方法锁。

4.同时访问同步方法与非同步方法:同步(并行)执行

synchronized关键字只作用于当前方法,不会影响其他未加关键字的方法的并发行为。因此非同步方法不受到影响,还是会并发执行。

5.访问同一个对象的不同的普通同步方法(对象锁):串行执行

synchronized关键字虽然没有指定所要的那个锁对象,但是本质上是指定了this这个对象作为它的锁。所以对于同一个实例来讲,两个方法拿到的是同一把锁,因此会出现串行的情况。

6.同时访问静态synchronized和非静态synchronized方法:同步(并行)执行

前者为类锁,锁为Class类;后者为对象锁,锁为this对象。因此两者的锁不同,会并行执行。

java 加锁_Java并发之synchronized深入相关推荐

  1. java排他锁关键字_Java并发之synchronized关键字深度解析(一)

    前言 近期研读路神之绝世武学,徜徉于浩瀚无垠知识之海洋,偶有攫取吉光片羽,惶恐未领略其精髓即隐入岁月深处,遂急忙记录一二,顺备来日吹cow之谈资.本小系列为并发之亲儿子-独臂狂侠synchronize ...

  2. java 等待_Java并发之等待/通知机制

    1 前言 本篇文章默认大家对synchronized跟ReentrantLock有一定了解. 1.1 先来段代码放松一下 下面一段简单的代码,主要是通过3个线程对count进行累计来进行模拟多线程的场 ...

  3. java 计数器_java并发之计数器CountDownLatch原理

    java并发之计数器CountDownLatch原理 CountDownLatch简介 CountDownLatch顾名思义,count + down + latch = 计数 + 减 + 门闩(这么 ...

  4. 深入理解Java并发之synchronized实现原理

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72828483 出自[zejian ...

  5. JAVA 的wait(), notify()与synchronized同步机制

    转自:http://blog.csdn.net/zyplus/article/details/6672775 在JAVA中,是没有类似于PV操作.进程互斥等相关的方法的.JAVA的进程同步是通过syn ...

  6. java线程同步——条件对象+synchronized 关键字

    [0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java线程同步--条件对象+synchronized 关键字 的相关知识: 0.2)for ...

  7. java 检视_Java高并发系列——检视阅读(五)

    JUC中工具类CompletableFuture CompletableFuture是java8中新增的一个类,算是对Future的一种增强,用起来很方便,也是会经常用到的一个工具类,熟悉一下. Co ...

  8. Java多线程学习十三:synchronized 和 Lock 区别以及孰优孰劣,如何选择?

    synchronized 和 Lock 的异同点,以及该如何选择. 相同点 synchronized 和 Lock 的相同点非常多,我们这里重点讲解 3 个比较大的相同点 ABC. A:synchro ...

  9. 操作系统锁的实现方法有哪几种_Java并发之Monitor实现

    Java并发之Monitor实现 可能在synchronized关键字的实现原理中,你已经知道了它的底层是使用Monitor的相关指令来实现的,但是还不清楚Monitor的具体细节.本文将让你彻底Mo ...

最新文章

  1. java pdf 中文_java-pdf-itext 生成pdf 文档(支持中文字体)
  2. Lucene.Net无障碍学习和使用:搜索篇
  3. 初学者学python好还是c-C 和 Python语言先学哪个好?
  4. Testin云测试:QQ(4.2.0)安卓版客户端可用性优秀
  5. 一天搞定CSS(扩展):CSS Hack
  6. 了解SQL和MySQL数据库
  7. nacos 公共_Nacos数据模型
  8. 图像极坐标变换及在OCR中的应用
  9. Linux 实操 —— Linux 系统性能分析
  10. Warning: Each record in table should have a unique `key` prop,or set `rowKey` to 解决方法
  11. 剑指offer面试题53 - II. 0~n-1中缺失的数字(二分查找)
  12. Mac 技巧——让Mac轻松访问Windows网络共享
  13. 一:Proficloud - EMMA能源管理+EMpro智能电表
  14. linux强制删除只读文件夹,强制删除文件夹linux的方法是什么
  15. 【微信小程序】关于小程序的协同工作与发布的工作流程
  16. IT运维大咖带你玩转企业信息运维自动化
  17. AQS与CLH相关论文学习系列(四)- AQS的设计思路
  18. [bzoj5473] 仙人掌
  19. Android studio插件GsonFormat 的使用
  20. 选择企业最合适的人才 —— 谈谈因人设岗与因事设岗

热门文章

  1. 年买笔记本的8个小技巧 最适合自己才最好(组图)
  2. H.264/AVC 标准中CAVLC 和CABAC 熵编码算法研究
  3. H.263 H.263+ Payload Type
  4. Linux下编译FFMpeg
  5. java: PO,VO,TO,BO,DAO,POJO 解释
  6. Docker 上安装、启动 MySQL (图解)
  7. python 类装饰器
  8. 通过扫码自定义链接安装iOS app,版本更新总结。
  9. Swift调用Objective C的FrameWork
  10. 走台阶 OR 台阶走——《狂人C》习题解答14(第三章习题4)