java多线程基础小白指南--synchronized同步块
sychronized是java多线程非常关键的一个知识点,这篇博客将从synchronized几个用法以及代码来学习。
sychronized的作用是能够保证同一时间只有一个线程来运行这块代码,达到并发效果,如果没有保证并发的话,在多线程编码中就会产生致命问题,比如经典的i++,这也是数据库并发中经典的案例,i++并不是原子操作,分为三步,取数,操作,写数,参考这段代码,可以运行一下看下结果
点击查看代码
public class showUnsafe1 implements Runnable{
static int i=0;
@Override
public void run() {
for(int j=0;j<10000;j++){
i++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new showUnsafe1());
Thread thread2 = new Thread(new showUnsafe1());
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(i);
}
}
synchronized四种用法
synchronized为啥这么神奇,无它,加锁而已,不少八股文喜欢分为两种锁,一种是对象锁,一种是类锁,还可以分为方法锁,代码块锁,静态锁,class锁,我们通过代码学习他们如何使用
- 对象锁:方法锁
方法锁是用synchronized修饰的一个类方法,作用方法即是方法作用域,除了这个方法要同步,其余不需要
两个线程访问同一对象的同一方法
public class SynchronizedObjectMethod implements Runnable{
private static SynchronizedObjectMethod instance=new SynchronizedObjectMethod();
public synchronized void method(){
System.out.println("我是对象锁的方法修饰符形式。我叫"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
@Override
public void run() {
method();
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while(thread1.isAlive()||thread2.isAlive()){
}
System.out.println("finish");
}
}
- 对象锁:代码块形式
代码块锁就是常用的同步方法块,synchronized锁住的是它里面的对象,作用域就是synchonized{}里面的代码
两个线程访问同一个对象的同步代码块
public class SynchronizedObjectCodeBlock implements Runnable{
private static SynchronizedObjectCodeBlock instance=new SynchronizedObjectCodeBlock();
Object lock1=new Object();
@Override
public void run() {
synchronized (lock1){
System.out.println("我是对象锁的代码块形式。我叫"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
}
public static void main(String[] args){
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
while(thread1.isAlive()||thread2.isAlive()){
}
System.out.println("finish");
}
}
- 类锁:class形式
class形式说的是synchronized()括号里使用的锁是class对象,所谓class对象指得是java文件对应的一个java.lang.class对象,所有该类生成的对象共有这个class对象(对于类加载机制可以看我另一篇文章https://www.cnblogs.com/spark-cc/p/16882268.html),所以这个锁锁住了这个类生成的所有对象
不同任务的两个线程访问同一个class对象
public class SynchronizedClassClass implements Runnable{
private static SynchronizedClassClass instance1=new SynchronizedClassClass();
private static SynchronizedClassClass instance2=new SynchronizedClassClass();
public void method(){
synchronized (SynchronizedClassClass.class){
System.out.println("我是类锁的形式之一:修饰.class。我叫"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
}
@Override
public void run() {
method();
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while(thread1.isAlive()||thread2.isAlive()){
}
System.out.println("finish");
}
}
在这个案例中,存在两个SynchronizedClassClass对象,但是不能同时访问同步代码
- 类锁:static形式
static形式说的是static修饰synchronized修饰的方法,即static synchronized methodName,作用方法还是这个类的class对象
注意静态同步方法和非静态方法
public class SynchronizedClassStatic implements Runnable{
private static SynchronizedClassStatic instance1=new SynchronizedClassStatic();
private static SynchronizedClassStatic instance2=new SynchronizedClassStatic();
public static synchronized void method(){
System.out.println("我是类锁的形式之一:加static修饰。我叫"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
public void method2(){
System.out.println("我是非静态方法,我叫"+Thread.currentThread().getName());
}
@Override
public void run() {
method();
method2();
}
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
while(thread1.isAlive()||thread2.isAlive()){
}
System.out.println("finish");
}
}
synchronized的原理
让我们从字节码来看看synchronized的神秘面纱
我们反编译synchronized修饰object的java文件
源代码
public class Decompilation {
private Object object=new Object();
public void insert(Thread thread){
synchronized (object){
}
}
}
反编译指令为 javap -c -v java文件的class文件

所以就是这个小小的monitorenter和monitorexit指令完成了同步操作
关于monitorenter和monitorexit的定义可以查看jvm文档(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.monitorenter) 中的注释

这是monitorenter的,注意看紫色框框起来的关键部分

这是monitorexit的
这里就解释清楚为什么synchronized上的锁是可重入的,对于更深入的理解,比如monitor的概念,cpp代码实现,我之后有机会再讲下我的理解(主要是我还没懂,不敢班门弄斧,怕了怕了)
我们再反编译synchronize修饰的同步方法,其结果是

与之前不一样,这里是在方法的访问标识上添加了ACC_SYNCHRONIZED,它在jvm文档中是这么解释

大致意思就是当调用设置了 ACC_SYNCHRONIZED 的方法时,执行线程进入监视器(monitor),然后执行这个方法,方法执行完毕后退出监视器。在执行线程拥有监视器期间,没有其他线程可以进入这个方法(这里也涉及到monitor的理解!!!)
synchornized的各种锁以及优化
之后进行补充
java多线程基础小白指南--synchronized同步块的更多相关文章
- java多线程(三)——锁机制synchronized(同步语句块)
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...
- Java 多线程基础(五)线程同步
Java 多线程基础(五)线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题. 要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制 ...
- “全栈2019”Java多线程第二十一章:同步代码块产生死锁的例子
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十八章:同步代码块双重判断详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java多线程编程实战指南(核心篇)读书笔记(五)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- [转]Java多线程干货系列—(一)Java多线程基础
Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...
- Java 多线程——基础知识
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程基础知识总结
2016-07-18 15:40:51 Java 多线程基础 1. 线程和进程 1.1 进程的概念 进程是表示资源分配的基本单位,又是调度运行的基本单位.例如,用户运行自己的程序,系统就创建一个进程, ...
- Java基础16:Java多线程基础最全总结
Java基础16:Java多线程基础最全总结 Java中的线程 Java之父对线程的定义是: 线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进 ...
- 《Java多线程编程实战指南(核心篇)》阅读笔记
<Java多线程编程实战指南(核心篇)>阅读笔记 */--> <Java多线程编程实战指南(核心篇)>阅读笔记 Table of Contents 1. 线程概念 1.1 ...
随机推荐
- 20192305 王梓全Python程序设计实验四报告
20192305 王梓全Python程序设计实验四报告 课程:<Python程序设计> 班级: 1923 姓名: 王梓全 学号:20192305 实验教师:王志强 实验日期:2021年6月 ...
- 平方损失函数为例的BP的关键公式推导
看了刘建平老师的博客https://www.cnblogs.com/pinard/p/6422831.html对如下其中两个公式进行详细推导 损失函数为(大写字母为矩阵,小写字母字母加粗为列向量,其中 ...
- PostgreSQL 存储过程 通过设定条件,返回指定的数据表记录
PL/pgSQL是 PostgreSQL 数据库系统的一个可装载的过程语言. PL/pgSQL的设计目标是创建一种可装载的过程语言,可以可用于创建函数和触发器过程, 在SQL语言中添加控制结构功能, ...
- ImmutablePair和ImmutableTriple的使用
场景 当我们调用某个方法,需要有超过一个值的返回时,我们通常会怎么做,比如现在需要调用方法返回姓名和年龄两个属性(假如姓名为张三,年龄为12). 方式一:使用Map,将Map中分别放入name为key ...
- scala概述入门和项目创建
1.scala简介 (1).scala基于JVM,与JAVA完全兼容,具有跨平台.可移植性好.方便的垃圾回收等特性: (2).scala比JAVA更加面向对象: (3).scala是一门函数式编程语言 ...
- Debug --> python list.sort()食用方法
list.sort(key=lambda x:x[1] , reverse=True) 参数 key 指明按照什么进行排序.lambda是匿名函数,参数的第一个x表示列表的第一个元素,如表示列表中的元 ...
- #Python #字符画 #灰度图 使用Python绘制字符画及其原理
由于最近身体状况不太好所以更新会有点慢,请大家多多包涵.同时也提醒大家注意保重身体! 前提:默认大家已经正确安装了 Python,且正确将Python配置到了系统Path . 目录 1.字符画的概况 ...
- Django框架搭建web项目(一)
建议查看官方文档:https://docs.djangoproject.com/zh-hans/4.0/intro/tutorial01/ 1.本地安装python环境(略) 2.本地安装Django ...
- springboot 整合内存缓存Caffeine
springboot 整合内存缓存Caffeine 1.引jar包 <dependency> <groupId>org.springframework.boot</gro ...
- 20181224《网络攻防技术》Exp 8 Web综合
20181224<网络攻防技术>Exp 8 Web综合 目录 20181224<网络攻防技术>Exp 8 Web综合 相关知识点总结 web前端 web后端 数据库编程 实验内 ...