前言:

  只要涉及到Java并发那么我们就会考虑线程安全,实际上能够实现线程安全的方法很多,今天先介绍一下synchronized关键字,主要从使用,原理介绍

一、synchronized的使用方法

  1、修饰代码块:大括号括起来的代码,作用于调用的对象

  2、修饰方法:整个方法,作用于调用的对象

下面来演示代码,并且理解这两个使用的方法

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class SynchronizedExample {
//使用synchronized修饰代码块
public void test1(int j) {
synchronized(this){
for(int i=0;i<10;i++) {
System.out.println("test->"+j+"->"+i);
}
}
}
//使用synchronized修饰方法
public synchronized void test2(int j) {
for(int i=0;i<10;i++) {
System.out.println("test->"+j+"->"+i);
}
}
public static void main(String args[]) {
SynchronizedExample example1=new SynchronizedExample();
SynchronizedExample example2=new SynchronizedExample();
ExecutorService e=Executors.newCachedThreadPool();
e.execute(()->{
example1.test1(1);
});
e.execute(()->{
example2.test1(2);
});
}
}

  上面的代码演示了如何修饰代码块和方法。这里主要说“作用于调用的对象”如何理解。上面两个线程分别执行两个对象中的test1方法,结果是两个线程会结合在一起执行,这是因为调用的对象不同,一个是example1对象,另一个是example2对象。如果同时调用example1对象中的test1和test2方法,那么会先执行test1再执行test2。就是这时候只在调用的对象相同的时候才会实现同步操作。结果如下图

  3、修饰静态方法:整个静态方法,作用于所有对象

  4、修饰类:括号起来的部分,作用于所有对象

  

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class SynchronizedExample {
//使用synchronized修饰类
public void test1(int j) {
synchronized(SynchronizeExample.class){
for(int i=0;i<10;i++) {
System.out.println("test->"+j+"->"+i);
}
}
}
//使用synchronized修饰静态方法
public static synchronized void test2(int j) {
for(int i=0;i<10;i++) {
System.out.println("test->"+j+"->"+i);
}
}
public static void main(String args[]) {
SynchronizedExample example1=new SynchronizedExample();
SynchronizedExample example2=new SynchronizedExample();
ExecutorService e=Executors.newCachedThreadPool();
e.execute(()->{
example1.test1(1);
});
e.execute(()->{
example2.test1(2);
});
}
}

  当理解了上面两条,下面的结果就很显然了。这里有点不同就是使用synchronized修饰类和修饰方法块时,后面的一个为当前类,一个为this,this可以理解为当前类的实例化对象,所以只对当前调用的对象有加锁。而锁一个类则是锁了当前类的所有实例化对象。即作用范围不一样。

二、synchronized原理

上面我们只是介绍了一下synchronized关键字的使用方法。接下来就是底层实现原理的介绍。

1、执行synchronized代码块的三个步骤:

  (1)、线程尝试获得锁,如果获得就进入第二步,否则就加入等待队列,阻塞并等待唤醒

  (2)、执行synchronized修饰部分的代码块

  (3)、线程释放锁。如果等待队列上有等待的线程,从中取一个并唤醒,但是不一定是按顺序的,也就是不保证公平性

2、JVM中的synchronized底层实现

  JVM是基于进入和退出monitor对象来实现方法同步和代码块同步。对于代码块同步:任何对象都有一个monitor与之关联,当一个monitor被持有后就会处于锁定状态,当线程执行到monitorenter指令时,就会尝试获得对象的monitor所有权。而反映到上面步骤即尝试获得锁。而后执行代码,当遇到monitorexit指令后就会释放锁。所以monitorexit指令一般放在方法结束处和异常处。上述这些指令都是JVM中编译后自动放置的。平常写代码的时候并不需要。

3、为什么synchronized作用的是对象

  在上面的使用方法中基本上都说了synchronized作用于对象,无论是调用的对象还是所有对象。为什么是作用于对象而不是代码块呢?是因为synchronized用的锁是存在Java对象头里的

  synchronized代表重量级锁(其实JDK有对synchronized进行各种优化,所以性能上有所提升。并不是说重量级锁就不使用了。)所以synchronized锁的是对象而不是代码块。

4、synchronized的可重入性

  synchronized是可重入的。可重入是通过记录锁的持有线程和持有数量来实现。当被调用synchronized保护的代码时,检查对象是否被锁,如果是再检查当前线程锁定,如果还是,那么增加持有数量。也就是上面例子中当一个线程拿到example1的锁之后,在没有释放锁的时候再次请求example1的锁是可以一定可以拿到的。这时候在JVM中关联的计数器就会加一,就相当于该线程持有锁的数量加一。而后每次执行一次monitorexit指令就会数量减一。当计数器为0的时候才会使得该线程释放对象锁(这里一定要弄清是对象而不是代码块,线程持有的是一个对象)。

5、synchronized的内存可见性

  synchronized是保证内存可见性的。在释放锁的时候,所有写入都会写回内存,获得锁后,就会从内存中读最新数据。但是若只为了保证内存可见性,简易使用volatile这个更加轻量级的锁。

四、总结

  其实重点强调的就是synchronized虽然是包裹着一个代码块,但是作用的一定是一个对象。这里并不是说线程拿到这个对象,这个对象就会被锁住,里面的其他代码都不可执行。这里意思是两个或多个线程尝试获得synchronized修饰的同一个代码块或方法,才会引起锁竞争。但是由于有时候作用的只是当前的调用对象,所以其他对象访问的时候也会也不会引起锁竞争,具体看使用方法的第一个例子。更重要还是去理解JVM中加入的monitorenter指令和monitorexit指令,会对理解synchronized有非常重要的帮助。同时说一下,当子类继承父类的时候,没有重写父类的synchronized方法,那么使用的时候是直接使用父类的方法,所以会同步。但是如果重写了方法却没有加上synchronized修饰。那么就不会有同步操作。所以子类是不会继承父类的synchronized锁。

Java并发——synchronized关键字的更多相关文章

  1. 精通java并发-synchronized关键字和锁

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages synchronized关键字和锁 示例代码 public class MyThreadTest2 { public ...

  2. Java并发-Synchronized关键字

    一.多线程下的i++操作的并发问题 package passtra; public class SynchronizedDemo implements Runnable{ private static ...

  3. Java并发—synchronized关键字

    synchronized关键字的作用是线程同步,而线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. synchronized用法 1. 在需要同步的方法的方法签名中加入synchro ...

  4. Java 多线程 —— synchronized关键字

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  5. 从分布式锁角度理解Java的synchronized关键字

    分布式锁 分布式锁就以zookeeper为例,zookeeper是一个分布式系统的协调器,我们将其理解为一个文件系统,可以在zookeeper服务器中创建或删除文件夹或文件.设D为一个数据系统,不具备 ...

  6. java基础Synchronized关键字之对象锁

    java中Synchronized关键字之对象锁    当有多个线程对一个共享数据进行操作时,需要注意多线程的安全问题. 多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同 ...

  7. Java的synchronized关键字:同步机制总结

    JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程 ...

  8. java中synchronized关键字分析

    今天我们来分析一下java中synchronized关键字.首先来看一段java代码:(本地编译环境为mac,jdk1.8的环境) Demo.java package com.example.spri ...

  9. Java基础-synchronized关键字的用法(转载)

    synchronized--同步 顾名思义是用于同步互斥的作用的. 这里精简的记一下它的使用方法以及意义: 当synchronized修饰 this或者非静态方法或者是一个实例的时候,所同步的锁是加在 ...

随机推荐

  1. 文本离散表示(一):词袋模型(bag of words)

    一.文本表示 文本表示的意思是把字词处理成向量或矩阵,以便计算机能进行处理.文本表示是自然语言处理的开始环节. 文本表示按照细粒度划分,一般可分为字级别.词语级别和句子级别的文本表示.字级别(char ...

  2. javascript正则表达式学习(二)--位置匹配

    文章首发于sau交流学习社区 一.前言 正则表达式是匹配模式,要么是匹配字符,要么匹配位置. 其实在开发中很少用到匹配位置,本篇文章主要包含: 二.什么是位置 位置:相邻字符之间的位置. 三.如何匹配 ...

  3. mysql数据库NO CONNECTION问题分析以及解决方案

    自己的站点有时候会挂掉,着实比较麻烦,我不会24小时都看着,说多的都是泪 出现mysql出现NO CONNECTION 有可能原因:mysql服务出现问题 解决方案:重启mysql服务,数据库就连接上 ...

  4. xamarin forms常用的布局StackLayout详解

    通过这篇文章你将了解到xamarin forms中最简单常用的布局StackLayout.至于其他几种布局使用起来,效果相对较差,目前在项目中使用最多的也就是这两种布局StackLayout和Grid ...

  5. 第一章.java&golang的区别之:闭包

    对于golang一直存有觊觎之心,但一直苦于没有下定决心去学习研究,最近开始接触golang.就我个人来说,学习golang的原动力是因为想要站在java语言之外来审视java和其它语言的区别,再就是 ...

  6. [翻译 EF Core in Action 1.7] MyFirstEfCoreApp访问的数据库

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  7. Java异常处理最佳实践及陷阱防范

    前言 不管在我们的工作还是生活中,总会出现各种“错误”,各种突发的“异常”.无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们 ...

  8. Webpack系列-第一篇基础杂记

    前言 公司的前端项目基本都是用Webpack来做工程化的,而Webpack虽然只是一个工具,但内部涉及到非常多的知识,之前一直靠CV来解决问题,之知其然不知其所以然,希望这次能整理一下相关的知识点. ...

  9. 如何通过免费开源的ERP Odoo打造企业全员营销整体解决方案

    应用场景的背景故事 在一些二级城市,往往线索的来源是通过企业当地口碑积累.熟人转介绍等线下的方式为主,利用互联网的模式往往很难奏效,企业面临的第一个问题就是如何把握线索真实的来源介绍的问题.在这个问题 ...

  10. Linux记录~持续更新~

    ls -ildha /etc -i 显示对应id号 唯一标识 -l 显示详情 -d 显示当前文件夹 不包括子目录 -h 单位为KB 而不是B -a 显示所有 包括隐藏文件 mkdir mkdir -p ...