在看源代码时遇到多线程需要同步的时候,总是会看见几种写法,修饰方法、修饰静态方法、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、方法的区别的更多相关文章

  1. 同步关键字synchronized

    同步关键字synchronized 同步关键字synchronized使用简洁,代码可维护性好.在JDK6中,性能也比早期的JDK有很大的改进.如果可以满足程序要求,应该首先考虑这种同步方式. 关键字 ...

  2. synchronized和volatile的使用方法以及区别

    先看看下面的例子: public class ThreadTest { public static void main(String[] args) { final Counter counter = ...

  3. Java中使用同步关键字synchronized需要注意的问题

    在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在 ...

  4. java多线程学习-同步(synchronized)

    (示例都是网上视频的) 假如两个线程同时调用一个方法输出字符串 public class SynchronizedTest extends Thread { public static void ma ...

  5. java中实现同步的两种方式:syschronized和lock的区别和联系

    Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...

  6. 多线程,线程同步,synchronized关键字的用法

    一.什么是多线程 Java多线程实现方式主要有四种:继承Thread类.实现Runnable接口.实现Callable接口通过FutureTask包装器来创建Thread线程.使用ExecutorSe ...

  7. Java:多线程,线程同步,synchronized关键字的用法(同步代码块、非静态同步方法、静态同步方法)

    关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨synchronized关键字. sy ...

  8. 5.同步关键字(synchronized)

    同步关键字(synchronized): 多线程给我们提供方便的时候,也给整个编程增加了难度,尤其是对临界资源的控制,尤为重要. 一个在操作系统课上,老掉牙的事例,就把这种情况解释的明明白白. 一对夫 ...

  9. 多线程总结-同步之synchronized关键字

    目录 1.为什么要使用synchronized? 2.synchronized锁什么,加锁的目的是什么? 3.代码示例 3.1锁this和临界资源对象 3.2锁class类对象 3.3 什么时候锁临界 ...

随机推荐

  1. Python学习---基础篇

    ###打开文件并打印: #!/usr/bin/python3 f = open('F://myproject/test.txt', encoding='utf-8',mode='r') content ...

  2. bootstrap栅格系统错位问题

    在项目中div可以设置属性class=“col-size-x” //size取值为xs,sm,md,lg:x取值为1-12 可以让此div占据本行的 x/12 .col-xs- 超小屏幕 手机 (&l ...

  3. MT【161】韦恩图

    (清华2017.4.29标准学术能力测试25) 若$N$的三个子集$A,B,C$满足$|A\cap B|=|B\cap C|=|C\cap A|=1$,且$A\cap B\cap C=\varnoth ...

  4. 洛谷3732:[HAOI2017]供给侧改革——题解

    https://www.luogu.org/problemnew/show/P3732 Anihc国提高社会生产力水平.落实好以人民为中心的发展思想.决定进行供给侧结构性改革. 为了提高供给品质.你调 ...

  5. Alpha 冲刺 —— 十分之五

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭鸭鸭鸭鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作 协助测试的进行 测试项目运行的服务器环境 ...

  6. Android 65535 问题与 MultiDex分包

    Android Multidex 遇到的问题 http://blog.csdn.net/wangbaochu/article/details/51178881 Android 使用android-su ...

  7. Android实现透明的颜色效果(zz)

    android Button或者ImageButton背景透明状态设置 设置Button或ImageButton的背景为透明或者半透明 半透明< Button android:backgroun ...

  8. bzoj3203【sdoi2013】保护出题人

    题目描述 输入格式 第一行两个空格隔开的正整数n和d,分别表示关数和相邻僵尸间的距离.接下来n行每行两个空格隔开的正整数,第i + 1行为Ai和 Xi,分别表示相比上一关在僵尸队列排头增加血量为Ai  ...

  9. linux expect 的使用

    expect 可以让一些需要交互的命令自动完成,如 ssh 连接的适合需要输入密码. 也就是说,某些重复的操作我们可以使用该命令完成,比如 ssh 连接到服务器,然后进到某个项目目录,进行 git 的 ...

  10. 四、Linux学习之文件处理命令

    1.建立目录:mkdir 格式:mkdir –p [目录名] -p     递归创建目录 注意事项: 如果是创建单个目录直接mkdir [目录名就可以] 如果是创建一个目录下的目录也就是递归创建目录请 ...