为什么要这么说呢, 因为笔者被这个坑过(其实是自己坑自己)╮(╯_╰)╭

先看一段synchronized 的详解:

synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

五、以上规则对其它对象锁同样适用.
 
简单来说, synchronized就是为当前的线程声明一个锁, 拥有这个锁的线程可以执行区块里面的指令, 其他的线程只能等待获取锁, 然后才能相同的操作.
 
这个很好用, 但是笔者遇到另一种比较奇葩的情况.
 
1. 在同一类中, 有两个方法是用了synchronized关键字声明
 
2. 在执行完其中一个方法的时候, 需要等待另一个方法(异步线程回调)也执行完, 所以用了一个countDownLatch来做等待
 
3. 代码解构如下:
synchronized void  a(){
countDownLatch = new CountDownLatch(1);
// do someing
countDownLatch.await();
} synchronized void b(){
countDownLatch.countDown();
}
 
 
 
其中
a方法由主线程执行, b方法由异步线程执行后回调
 
执行结果是:
主线程执行 a方法后开始卡住, 不再往下做, 任你等多久都没用.
 
这是一个很经典的死锁问题
 
a等待b执行, 其实不要看b是回调的, b也在等待a执行. 为什么呢? synchronized 起了作用.
 
一般来说, 我们要synchronized一段代码块的时候, 我们需要使用一个共享变量来锁住, 比如:
byte[]  mutex = new byte[0];

void a1(){
synchronized(mutex){
//dosomething
}
} void b1(){ synchronized(mutex){
// dosomething
} }
 
如果把a方法和b方法的内容分别迁移到 a1和b1 方法的synchronized块里面, 就很好理解了.
 
a1执行完后会间接等待(countDownLatch)b1方法执行
 
然而由于 a1 中的mutex并没有释放, 就开始等待b1了, 这时候, 即使是异步的回调b1方法, 由于需要等待mutex释放锁, 所以b方法并不会执行
于是就引起了死锁
 
而这里的synchronized关键字放在方法前面, 起的作用就是一样的. 只是java语言帮你隐去了mutex的声明和使用而已. 同一个对象中的synchronized 方法用到的mutex是相同的, 所以即使是异步回调, 也会引起死锁, 所以要注意这个问题. 这种级别的错误是属于synchronized关键字使用不当. 不要乱用, 而且要用对.
 
那么这样的 隐形的mutex 对象究竟是 什么呢?
 
很容易想到的就是 实例本身. 因为这样就不用去定义新的对象了做锁了. 为了证明这个设想, 可以写一段程序来证明.
 
思路很简单, 定义一个类, 有两个方法, 一个方法声明为 synchronized, 一个在 方法体里面使用synchronized(this), 然后启动两个线程, 来分别调用这两个方法, 如果两个方法之间发生锁竞争(等待)的话, 就可以说明 方法声明的 synchronized 中的隐形的mutex其实就是 实例本身了.
 
public class MultiThreadSync {

    public synchronized void m1() throws InterruptedException{
System. out.println("m1 call" );
Thread. sleep(2000);
System. out.println("m1 call done" );
} public void m2() throws InterruptedException{
synchronized (this ) {
System. out.println("m2 call" );
Thread. sleep(2000);
System. out.println("m2 call done" );
}
} public static void main(String[] args) {
final MultiThreadSync thisObj = new MultiThreadSync(); Thread t1 = new Thread(){
@Override
public void run() {
try {
thisObj.m1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}; Thread t2 = new Thread(){
@Override
public void run() {
try {
thisObj.m2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}; t1.start();
t2.start(); } }
 
结果输出是:
 
m1 call
m1 call done
m2 call
m2 call done
 
说明方法m2的sync块等待了m1的执行. 这样就可以证实 上面的设想了.
 
另外需要说明的是, 当sync加在 static的方法上的时候, 由于是类级别的方法, 所以锁住的对象是当前类的class实例. 同样也可以写程序进行证明.这里略.
 
所以方法的synchronized 关键字, 在阅读的时候可以自动替换为synchronized(this){}就很好理解了.
                                        void method(){
void synchronized method(){ synchronized(this){
// biz code // biz code
} ------>>> }
}

Java_慎用方法级别的synchronized关键字的更多相关文章

  1. 改代码不是很熟悉------方法上加入synchronized关键字,会有性能问题---如何改善

    package com.bjpowernode.t14; import java.time.Duration;import java.time.LocalTime; public class Proc ...

  2. volatile与synchronized关键字

    volatile关键字相信了解Java多线程的读者都很清楚它的作用.volatile关键字用于声明简单类型变量,如int.float.boolean等数据类型.如果这些简单数据类型声明为volatil ...

  3. java synchronized关键字浅析

    synchronized这个关键字想必学Java的人都应该知道. 直接上例子: 方法级别实例 public class AtomicInteger { private int index; publi ...

  4. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  5. Java 多线程 —— synchronized关键字

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  6. synchronized关键字小结(一)

    1. synchronized同步方法 1) synchronized修饰方法,表示方法是同步的,当某线程进入并拿到当前整个对象的锁时 a. 其他synchronized方法排队等锁 b. 非sync ...

  7. Java多线程初学者指南(10):使用Synchronized关键字同步类方法

    要想解决“脏数据”的问题,最简单的方法就是使用synchronized关键字来使run方法同步,代码如下: public synchronized void run() { ... } 从上面的代码可 ...

  8. java基础回顾(五)线程详解以及synchronized关键字

    本文将从线程的使用方式.源码.synchronized关键字的使用方式和陷阱以及一些例子展开java线程和synchronized关键字的内容. 一.线程的概念 线程就是程序中单独顺序的流控制.线程本 ...

  9. Java并发之synchronized关键字

         上篇文章我们主要介绍了并发的基本思想以及线程的基本知识,通过多线程我们可以实现对计算机资源的充分利用,但是在最后我们也说明了多线程给程序带来的两种典型的问题,针对它们,synchronize ...

随机推荐

  1. go语言项目汇总

    Horst Rutter edited this page 7 days ago · 529 revisions Indexes and search engines These sites prov ...

  2. Autofac Named命名和Key Service服务

    参考:http://www.cnblogs.com/wolegequ/archive/2012/06/03/2532605.html

  3. Windows 8 应用程序前后台切换事件监听

    在一些情况下,我们需要监听应用程序切换到后台或者从后台切换至前台的事件,从而进行相关处理操作.支付宝应用锁屏(IOS,Android平台)的处理中就需要监听此事件,在用户将应用切换至后台一段时间后再切 ...

  4. Linux入门(二)Shell基本命令

    上一篇讲了普通用户切换到root用户,今天补充一点,对于Debian和Ubuntu用户,安装时候只有一个普通用户注册,在需要root权限时,我们可以在普通用户模式下输入sudo这个命令运行某些相关特权 ...

  5. Struts – MappingDispatchAction Example

    Struts MappingDispatchAction class is used to group similar functionality into a single action class ...

  6. AutoCompleteTextView,Spinner,消息提示

    package com.example.wang.testapp2; import android.app.Notification; import android.app.NotificationM ...

  7. Asp.net MVC NPOI导出Excel

    public class NpoiMemoryStream : MemoryStream { public NpoiMemoryStream() { AllowClose = true; } publ ...

  8. Asp.net Vnext 实现IView

    概述 Iview定义很简单,就是根据View上下文和TextWriter对象实现对View的呈现. 实现 实现IViewEngine public class TestViewEngine : IVi ...

  9. java.lang.NoClassDefFoundError: ognl/PropertyAccessor

    本篇对 Web 开发中,项目部署后.开启 Tomcat 服务器 Console 控制台报错 java.lang.NoClassDefFoundError: ognl/PropertyAccessor ...

  10. openfire 部署后报错: java.lang.IllegalArgumentException: interface xx is not visible from class loader

    该异常是创建代理时加载接口的类加载器与创建时传入的不一致. 在本地eclipse做openfire二次开发,本地运行没错,部署到服务器上后报异常:  java.lang.IllegalArgument ...