Java并发编程原理与实战九:synchronized的原理与使用
一、理论层面
内置锁与互斥锁
修饰普通方法、修饰静态方法、修饰代码块
package com.roocon.thread.t3;
public class Sequence {
private static int value;
// synchronized放在普通方法上,内置锁就是当前方法的实例
public synchronized int getNext(){
return value++;
}
// synchronized修饰静态方法,内置锁就是当前的Class字节码对象Sequence.class
public static synchronized int getPrevious(){
return value--;
}
public int xx(){
// synchronized修饰静态代码块,则锁的是任意一个对象
/*
synchronized (this){ }
synchronized (Integer.valueOf(value)) { }
synchronized (Sequence.class) { }
*/
synchronized (Sequence.class) {
if (value > 0) {
return value;
}else {
return -1;
}
}
}
public static void main(String[] args) {
Sequence sequence = new Sequence();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+" "+sequence.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
二、JVM层面

查看xx方法同步代码块字节码:

查看同步方法字节码:

任何对象都可以作为锁,那么锁信息又存在对象的什么地方呢?
存在对象头中。
对象头中的信息:
Mark Word
Class Metadata Address 类的类型地址
Array Length
1.偏向锁:
每次获得锁和释放锁会浪费资源(消耗时间),很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。线程在获取锁时,会依据对象头Mark word信息进行判断执行。
对象头Mark word中会保存如下信息:
线程id
Epoch
对象的分代年龄信息
是否是偏向锁
锁标志位
偏向锁在获取锁之后,如果没有竞争,也就是一直是这个线程在获取锁,那么当这个线程第二次再来进入该方法时,不需要再去获取锁了,也不需要释放锁,这样,就节省了大量的获取锁释放锁的资源。那么,什么时候会释放锁呢?只有当存在竞争时,才会去释放锁。
偏向锁适用于什么场景?
只有一个线程在访问同步代码块的场景。
2.轻量级锁:可以同时让多个线程进入同步代码块。自旋锁就是轻量级锁。
轻量级锁是如何加锁的?
在线程执行同步代码块之前,jvm会先在当前线程的栈帧中创建用于存储锁记录的存储空间。
(栈帧是什么?虚拟机栈中存储的是一个一个的栈帧,栈帧中存储了方法的执行信息,每个方法都会伴随着栈帧的进栈和出栈)
然后呢,并将对象头中的mark word复制到锁记录中。然后呢,开始竞争锁就可以了。竞争成功之后,markword就改变了,会将锁标志位改成轻量级锁。接着,开始执行同步体。
另外一个线程也想获得该锁。同样,它也将对象头中的mark word复制到锁记录中,它发现已经被其他线程获得了锁,所以它修改不成功。于是,它就不停的去修改,不停的失败,直到第一个线程把这个锁释放了,它就可以修改成功了。刚才这一个过程,就是所谓的自旋锁。
3.重量级锁
什么是重量级锁?就是,这个线程获得锁进入之后,其他线程必须在外面等待。synchronized就是重量级锁。
三、总结
上面只是对synchronized 如何实现同步功能的一个视频学习总结,更加细节的深入原理可以参考下面两篇文章:
参考资料:
《java并发编程与实战》龙果学院
Java并发编程原理与实战九:synchronized的原理与使用的更多相关文章
- Java并发编程系列-(8) JMM和底层实现原理
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...
- Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析
相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...
- Java并发编程知识点总结Volatile、Synchronized、Lock实现原理
Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...
- 【Java并发编程之深入理解】Synchronized的使用
原文:https://blog.csdn.net/zjy15203167987/article/details/82531772 1.为什么要使用synchronized 在并发编程中存在线程安全问题 ...
- 转:【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
Executor框架简介 在Java5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.coc ...
- 转:【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17199201 在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访 ...
- 【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
在并发编程中,多线程同时并发访问的资源叫做临界资源,当多个线程同时访问对象并要求操作相同资源时,分割了原子操作就有可能出现数据的不一致或数据不完整的情况,为避免这种情况的发生,我们会采取同步机制,以确 ...
- java并发编程学习: 阻塞队列 使用 及 实现原理
队列(Queue)与栈(Stack)是数据结构中的二种常用结构,队列的特点是先进先出(First In First Out),而Stack是先进后出(First In Last Out),说得通俗点: ...
- java并发编程的艺术(四)---ConcurrentHashMap原理解析
本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...
- Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语
Java 5 引入的 Concurrent 并发库软件包中,提供了 ReentrantLock 可重入同步锁,用来替代 synchronized 关键字原语,并可提供更好的性能,以及更强大的功能.使用 ...
随机推荐
- 浅谈Java中的Hashmap
HashMap: java.lang.Object ∟ java.util.AbstractMap<K,V> ∟ java.util.HashMap<K,V> 类型参数: ...
- unix网络编程——I/O多路复用之epoll
1. 基本概念 当程序进行IO时,如果数据尚未准备好,那么IO将处于阻塞状态.当某个进程有多个打开的文件,比如socket,那么其后的所有准备好读写的文件将受到阻塞的影响而不能操作.不借助线程,单一进 ...
- 树莓派与Arduino Leonardo使用NRF24L01无线模块通信之基于RF24库 (五) 树莓派单子节点发送数据
本项目中各个节点和树莓派的通信不区分信道,因此如果由树莓派发送给特定节点的数据会被所有节点接收到,因此子节点可以判别该数据是否发给自己的,需要在数据的第二个字节中加入目标节点的编号(第一个字节为源节点 ...
- testNg-build.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <proje ...
- php推送
需求: 我想做个会员站内通知的功能.不想用以前的ajax查询,听说有个推技术.以下文章介绍的不错,来自转载, ============================================= ...
- MSTSC 修改端口的简单方法 3389
1. 3389端口太过危险 最简单的办法是 修改默认端口方法非常简单. 2. win+r 打开运行, 输入 regedit 打开 注册表 3. 在地址栏输入 远程的服务的路径 输入的内容为: 计算机 ...
- Java乐观锁、悲观锁
乐观锁 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号 ...
- 浅谈JavaScript预编译原理
这两天又把js的基础重新复习了一下,很多不懂得还是得回归基础,大家都知道js是解释性语言,就是编译一行执行一行,但是在执行的之前,系统会做一些工作: 1,语法分析: 2,预编译: 3,解释执行. 语法 ...
- 重新认识javascript的settimeout和异步
1.简单的settimeout setTimeout(function () { while (true) { } }, 1000); setTimeout(function () { alert(' ...
- Django 2.0 学习(21):Django Session
Django Session Session 与Cookie 1.简介 1.Cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们又需要"保持状态",因 ...