Java synchronized 关键字详解
Java synchronized 关键字详解
前置技能点
- 进程和线程的概念
- 线程创建方式
- 线程的状态状态转换
- 线程安全的概念
synchronized 关键字的几种用法
修饰非静态成员方法
synchronized public void sync(){ }
修饰静态成员方法
synchronized public static void sync(){ }
类锁代码块
synchronized (类.class){ }
对象锁代码块
synchronized (this|对象){ }
synchronized 修饰非静态方法时可以看做是锁 this 对象,修饰静态方法时可以看做是锁方法所在的类。
synchronized 关键字的根本机制
各个线程想要访问被 synchronized 修饰的代码块,就要取得 synchronized 声明的锁。如果两个线程的目标是同一个锁,就会出现阻塞的现象,所以两个线程不能同时访问同一个锁下的代码,保证了多线程在执行时最终结果不会出错。这与共享变量是否为静态无关。
几个例子
对象锁
public class ThreadDemo extends Thread {
@Override
public synchronized void run() {
for (int i = 0; i < 10000; i++) {
Main.i++;
}
System.out.println("执行完成");
}
}
直接将继承的 run() 方法标记为 synchronized ,作用是对 Main 类中的 i 变量做 10000 次累加操作。
public class Main {
static int i = 0;
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo=new ThreadDemo();
Thread t1 = new Thread(threadDemo);
Thread t2 = new Thread(threadDemo);
Thread t3 = new Thread(threadDemo);
Thread t4 = new Thread(threadDemo);
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println(i);
}
}
//输出结果:
//执行完成
//执行完成
//执行完成
//执行完成
//40000
可以看到当4个线程全部执行完毕之后,变量 i 成功的累加了 40000 次,没有出现丢失操作的情况。
如果我们将 main() 方法修改如下:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadDemo();
Thread t2 = new ThreadDemo();
Thread t3 = new ThreadDemo();
Thread t4 = new ThreadDemo();
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println(i);
}
//输出结果:
//执行完成
//执行完成
//执行完成
//执行完成
//27579
可以看到丢失了不少的累加操作。观察前后两个 main() 方法创建线程的方式可以发现,前面的 main() 方法是使用了同一个对象来创建了4个不同的线程,而后一个 main() 方法使用了4个不同的 ThreadDemo 对象创建了4个线程。我们用 synchronized 修饰的是一个非静态成员函数,相当于对该方法创建了 this 的对象锁。在第一个 main() 方法中使用同一个对象来创建 4 个不同线程就会让 4 个线程争夺同一个对象锁,这样,在同一时间内,仅能有一个线程能访问 synchronized 修饰的方法。而在第二种 main() 方法中,4 个线程各自对应一个对象锁,4 个线程之间没有竞争关系,对象锁自然无法生效。
类锁
public class ThreadDemo extends Thread {
@Override
public void run() {
synchronized (ThreadDemo.class) {
for (int i = 0; i < 10000; i++) {
Main.i++;
}
System.out.println("执行完成");
}
}
}
将修饰方法的 synchronized 改为对 ThreadDemo.class 上锁的代码块
public class ThreadDemo2 extends Thread {
@Override
public void run() {
synchronized (ThreadDemo2.class) {
for (int i = 0; i < 10000; i++) {
Main.i++;
}
System.out.println("执行完成");
}
}
}
再创建一个相同的类命名为 ThreadDemo2 ,与 ThreadDemo 不同的是,ThreadDemo2 中,synchronized 对 ThreadDemo2.class 上锁。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadDemo();
Thread t2 = new ThreadDemo();
Thread t3 = new ThreadDemo2();
Thread t4 = new ThreadDemo2();
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
System.out.println(i);
}
//输出结果:
//执行完成
//执行完成
//执行完成
//执行完成
//33054
4 个线程分别由 ThreadDemo 和 ThreadDemo2 来创建,显然得到的结果与预期的 40000 不符。如果我们将 ThreadDemo2 中的 synchronized 改为对 ThreadDemo.class 上锁:
public class ThreadDemo2 extends Thread {
@Override
public void run() {
synchronized (ThreadDemo.class) {
for (int i = 0; i < 10000; i++) {
Main.i++;
}
System.out.println("执行完成");
}
}
}
//输出结果:
//执行完成
//执行完成
//执行完成
//执行完成
//40000
可以看到,虽然是声明在两个不同的类中的 synchronized 代码块,但是由于都是对 ThreadDemo.class 上锁,所以 4 个线程之间还是建立了竞争关系,同时只能有一个线程访问被 synchronized 修饰的代码。
总结
所以 synchronized 关键字的本质是限制线程访问一段代码,而限制的条件就是,在所有被加上相同锁的代码上,同一时间,只能有一个线程在运行。这与你要修改什么样的共享变量无关。在我刚接触到的时候以为类锁和对象锁是分别针对静态共享变量和非静态共享变量的,但事实上锁的是要执行的代码块,而不是代码块将要访问的共享变量。
Java synchronized 关键字详解的更多相关文章
- [java] java synchronized 关键字详解
Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一 ...
- Java多线程(三)—— synchronized关键字详解
一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...
- “全栈2019”Java多线程第十六章:同步synchronized关键字详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java volatile关键字详解
Java volatile关键字详解 volatile是java中的一个关键字,用于修饰变量.被此关键修饰的变量可以禁止对此变量操作的指令进行重排,还有保持内存的可见性. 简言之它的作用就是: 禁止指 ...
- Java 多线程(六) synchronized关键字详解
多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题. 同步机制可以使用synchronized关键字实现. 当synchroniz ...
- 从线程池到synchronized关键字详解
线程池 BlockingQueue synchronized volatile 前段时间看了一篇关于"一名3年工作经验的程序员应该具备的技能"文章,倍受打击.很多熟悉而又陌生的知识 ...
- java synchronized 同步详解
记下来,很重要. Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchron ...
- 简单的互斥同步方式——synchronized关键字详解
目录 1. 关于synchronized关键字 2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 2.2 synchronized是如何实现线程互斥访问 ...
- synchronized关键字详解(二)
synchronized关键字的性质 1.可重入:同一线程的外层函数获得锁之后,内层函数可直接再次获得该锁,好处:避免死锁,提升封装性 证明可重入粒度:1.同一个方法是可重入的 2.可重入不要求是同一 ...
随机推荐
- .net core ef动态orderby
前言 最近在给大家写一套开源的.net core权限管理框架.现在已经写到前台UI + 后台动态查询的部分. 发现需要动态orderby但是网上没有现成的例子 二话不说上代码 建议namespace ...
- MySQL数据库之xtrabackup物理备份(一)
前言:说到数据库备份,我们知道可以用来对数据库进行备份的工具有mysqldump.mydumer.mysqlpump等等,实际工作中,机器上的数据库不大的话,都是用mysqldump工具来备份,这些备 ...
- MNIST数据集
一.MNIST数据集分类简单版本 import tensorflow as tffrom tensorflow.examples.tutorials.mnist import input_data # ...
- 史上最详细的二叉树、B树,看不懂怨我
今天我们要说的红黑树就是就是一棵非严格均衡的二叉树,均衡二叉树又是在二叉搜索树的基础上增加了自动维持平衡的性质,插入.搜索.删除的效率都比较高.红黑树也是实现 TreeMap 存储结构的基石. 1.二 ...
- jQuery学习总结(三)
这篇文章讲的是jQuery里的ajax发送data的三种方式,利用ajax发送数据的好处是把数据发送到了servlet后,当前页面不进行跳转. jQuery的里的ajax发送data的方式主要有三种, ...
- ios---apple mach-o linker error 报错解决
问题触发场景 在新xcode环境里配置了cocoapods,并运行了自己的项目.然后某日从其他地方clone了第三方项目,打开后,有了这个报错: 问题原因 1.用cocoapods装了第三方插件后,要 ...
- 集合详解之 Map
集合详解之 Map + 面试题 集合有两个大接口:Collection 和 Map,本文重点来讲解集合中另一个常用的集合类型 Map. 以下是 Map 的继承关系图: Map 简介 Map 常用的实现 ...
- Nginx(3)---代理与负载均衡
一.代理简述 代理分为正向代理和反向代理, 正向代理:客户端与目标服务器之间增加一个代理服务器,客户端直接访问代理服务器,在由代理服务器访问目标服务器并返回客户端并返回 .比如夜深人静的时候访问的一些 ...
- OS课程 ucore_lab2实验报告
练习零:填写已有实验 本实验依赖实验1.请把你做的实验1的代码填入本实验中代码中有"LAB1"的注释相应部分.提示:可采用diff和patch工具进行半自动的合并(merge ...
- Qt实现简易计算器
麻烦到不能再麻烦的实现,简单到不能再简单的思路. calc.h #ifndef CALC_H #define CALC_H #include <QtWidgets/QMainWindow> ...