新手向-同步关键字synchronized对this、class、object、方法的区别
在看源代码时遇到多线程需要同步的时候,总是会看见几种写法,修饰方法、修饰静态方法、
synchronized(Xxx.class)、synchronized(this)、synchronized(obj),之前一直没深究几种方式的区别,现在想来真是惊出一身冷汗,居然这个问题都没有仔细想清楚。
synchronized的语义
每个对象都有一个监视器monitor,被synchronized修饰,语义就是获取这个对象的monitor,反编译后可以看到monitorenter和monitorexit。synchronized关键字有三种应用方式(其实按标题来讲应该是5种,但是其中有两种都是与另外两种等价的):
- 修饰实例方法
- 修饰静态方法
- 修饰代码块(指定对象)
实验
先上代码做实验验证一下
public class SyncThread {
private final Object lock = new Object();
public void foo() throws Exception {
synchronized (SyncThread.class) {
for (int i = 0; i < 5; i++) {
System.out.println(">>>foo: " + i);
Thread.sleep(1000);
}
}
}
public void bar() throws Exception {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println("<<<bar: " + i);
Thread.sleep(1000);
}
}
}
public void cpp() throws Exception {
synchronized (lock) {
for (int i = 0; i < 5; i++) {
System.out.println("===cpp: " + i);
Thread.sleep(1000);
}
}
}
public void der() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println("!!!der: " + i);
Thread.sleep(1000);
}
}
}
可以看到有四种不同的synchronized修饰,以及一个没有同步的方法,再运行一下看看结果
public class ThreadApp {
public static void main(String[] args) {
final SyncThread syncThread = new SyncThread();
new Thread(() -> {
try {
syncThread.foo();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
syncThread.bar();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
syncThread.cpp();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
syncThread.der();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
可以看到结果
>>>foo: 0
<<<bar: 0
===cpp: 0
!!!der: 0
>>>foo: 1
===cpp: 1
<<<bar: 1
!!!der: 1
>>>foo: 2
!!!der: 2
<<<bar: 2
===cpp: 2
>>>foo: 3
<<<bar: 3
===cpp: 3
!!!der: 3
>>>foo: 4
!!!der: 4
<<<bar: 4
===cpp: 4
分析
从以上结果来看各线程并没有发生竞争,互不影响,其实明白了synchronized语义也很好理解以上结果,几个synchronized获取的monitor都不是一个,当然相互不影响。
但是值得注意的几点:
synchronized(Xxx.class)获取的是类的monitor,所以与public synchronized static void some()修饰静态方法是等价的synchronized(this)获取的是当前实例的monitor,所以与public synchronized void some()修饰实例方法是等价的
以上两点可以通过修改上述代码中方法可以很容易验证,我们修改最后一个方法
public synchronized static void der() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println("!!!der: " + i);
Thread.sleep(1000);
}
}
运行得到结果
!!!der: 0
!!!der: 1
!!!der: 2
!!!der: 3
!!!der: 4
>>>foo: 0
>>>foo: 1
>>>foo: 2
>>>foo: 3
>>>foo: 4
可以看到,确实synchronized(Xxx.class)与synchronized修饰静态方法是等价的。再修改为synchronized修饰实例方法
public synchronized void der() throws Exception {
for (int i = 0; i < 5; i++) {
System.out.println("!!!der: " + i);
Thread.sleep(1000);
}
}
运行查看结果
<<<bar: 0
<<<bar: 1
<<<bar: 2
<<<bar: 3
<<<bar: 4
!!!der: 0
!!!der: 1
!!!der: 2
!!!der: 3
!!!der: 4
可以看到,确实synchronized(this)与synchronized修饰实例方法是等价的。
总之,个人认为要理解几种不一样的地方,关键是理解清楚是获取的谁的monitor,只要是同一个monitor,当然就会发生同步!
新手向-同步关键字synchronized对this、class、object、方法的区别的更多相关文章
- 同步关键字synchronized
同步关键字synchronized 同步关键字synchronized使用简洁,代码可维护性好.在JDK6中,性能也比早期的JDK有很大的改进.如果可以满足程序要求,应该首先考虑这种同步方式. 关键字 ...
- synchronized和volatile的使用方法以及区别
先看看下面的例子: public class ThreadTest { public static void main(String[] args) { final Counter counter = ...
- Java中使用同步关键字synchronized需要注意的问题
在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在 ...
- java多线程学习-同步(synchronized)
(示例都是网上视频的) 假如两个线程同时调用一个方法输出字符串 public class SynchronizedTest extends Thread { public static void ma ...
- java中实现同步的两种方式:syschronized和lock的区别和联系
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...
- 多线程,线程同步,synchronized关键字的用法
一.什么是多线程 Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorSe ...
- Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...
- 5.同步关键字(synchronized)
同步关键字(synchronized): 多线程给我们提供方便的时候,也给整个编程增加了难度,尤其是对临界资源的控制,尤为重要. 一个在操作系统课上,老掉牙的事例,就把这种情况解释的明明白白. 一对夫 ...
- 多线程总结-同步之synchronized关键字
目录 1.为什么要使用synchronized? 2.synchronized锁什么,加锁的目的是什么? 3.代码示例 3.1锁this和临界资源对象 3.2锁class类对象 3.3 什么时候锁临界 ...
随机推荐
- BZOJ5006 THUWC2017随机二分图(概率期望+状压dp)
下称0类为单边,1类为互生边,2类为互斥边.对于一种匹配方案,考虑其出现的概率*2n后对答案的贡献,初始为1,如果有互斥边显然变为0,否则每有一对互生边其贡献*2.于是有一个显然的dp,即设f[S1] ...
- C 输入 & 输出——Day03
当我们提到输入时,这意味着要向程序填充一些数据.输入可以是以文件的形式或从命令行中进行.C 语言提供了一系列内置的函数来读取给定的输入,并根据需要填充到程序中. 当我们提到输出时,这意味着要在屏幕上. ...
- 安装linux系统后调优及安全设置
环境说明: [root@server1 ~]# cat /etc/redhat-release CentOS release 6.9 (Final) [root@server1 ~]# uname - ...
- zookeeper和PHP zookeeper和kafka 扩展安装
http://blog.csdn.net/fenglailea/article/details/52458737#t3 目录(?)[-] 安装zookeeper 1直接安装zookeeper无须编 ...
- Redis事务介绍
概述 相信学过Mysql等其他数据库的同学对事务这个词都不陌生,事务表示的是一组动作,这组动作要么全部执行,要么全部不执行.为什么会有这样的需求呢?看看下面的场景: 微博是一个弱关系型社交网络,用户之 ...
- python 冒泡法 排序
冒泡排序 冒泡排序(Bubble Sort):重复地遍历要排序的数列,依次比较两个元素,如果他们的顺序不符就把他们交换过来.就像气泡一样,需要排序的元素通过比较.交换位置,一点一点浮到对应的位置. 个 ...
- 解决Maven提示:Could not read settings.xml
在Eclipse中配置maven时,提示错误:Could not read settings.xml.用户配置无法生效. 1.首先检查自己的settings.xml配置文件,发现在<!----& ...
- 触发器的SQL语法
create trigger triggerName after/before insert/update/delete on 表名 for each row #这句话在mysql是固定的 begin ...
- U45490 还没想好名字的题Ⅱ
这一题的环状板 Solution 暴力断环为链, 枚举起点跑 \(n\) 遍 \(DP\), 取最小值即可 Code #include<iostream> #include<cstd ...
- mongo查询日期格式数据
/ali/mongodb/bin/mongo -u user -p '123456' 127.0.0.1:27017/KYElog ISODate方式 db.col_02.find({"Lo ...