浅析synchronized关键字

因为程序中多线程场景的存在,所以需要采用synchronized关键字赖规避并发问题.

synchronized关键字能作用的范围有类,方法,静态方法,代码块.
1.修饰类:作用的对象就是这个类的所有对象.

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

2.修饰方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用该方法的那个对象.由此可见,作用到方法的同步关键字比作用到类的同步关键字的力度要小.但是同步关键字不能修饰接口方法,同样也不修饰构造方法,同步关键字也不能被继承.
当线程在访问某个对象的一个同步方法时,那么其他线程不能访问该对象的其他同步线程.一个对象实例,只能加持一个锁.

public synchronized void method()
{
   // todo
}

3.修饰静态方法:作用的范围是整个方法,作用的对象是该类的所有对象.总所周知,静态方法是属于类的,所以同步关键字是锁定的这个类的所有对象.

public synchronized static void method() {
   // todo
}

4.修饰同步代码块:作用范围是{}包括的那部分代码,作用的对象是调用这块代码的对象.

  • 当一个线程访问一个对象的一个synchronized(this)同步代码块时,其他线程对object中的其他synchronized代码块也不能访问.

Synchronized同步代码块底层原理

创建一个synchronized代码demo:
/**
 * Created by xueyunlong on 17-9-12.
 *
 */
public class SynchronizedDemo {

    public void test(){
        synchronized (this){
            System.out.println("同步代码块");
        }
    }

}

对上述类先进行编译,再进行反编译.
javac -g SynchronizedDemo.java
javap -c SynchronizedDemo.class
得到反编译结果为:

Compiled from "SynchronizedDemo.java"
public class SynchronizedDemo {
  public SynchronizedDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void test();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3                  // String 同步代码块
       9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: aload_1
      13: monitorexit
      14: goto          22
      17: astore_2
      18: aload_1
      19: monitorexit
      20: aload_2
      21: athrow
      22: return
    Exception table:
       from    to  target type
           4    14    17   any
          17    20    17   any
}

其中,同步关键字的实现是通过monitorenter和monitorexit实现的.其大致原理如下:
1.每个对象都一个monitor监视器锁,每次请求该代码块的时候即执行monitorenter时,都会先判断monitor的值是否大于1,大于1说明有线程占用,阻塞.
2.执行monitorexit,monitor的进入数减1,当进入数等0时,线程退出monitor.

Synchronized同步方法底层原理

public class com.zejian.concurrencys.SyncMethod
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool;

   //省略没必要的字节码
  //==================syncTask方法======================
  public synchronized void syncTask();
    descriptor: ()V
    //方法标识ACC_PUBLIC代表public修饰,ACC_SYNCHRONIZED指明该方法为同步方法
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field i:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field i:I
        10: return
      LineNumberTable:
        line 12: 0
        line 13: 10
}
SourceFile: "SyncMethod.java"

1.在同步方法的实现中,并没有像同步代码块中的没有monitorenter指令和monitorexit指令,而是通过ACC_SYNCHRONIZED访问标志来标识一个方法的同步性.

synchromized与ReentrantLock的对比

JDK1.6之后,虚拟机对Synchronized关键字进行了整体优化,在整体性能上两者基本上没有太大的差距.所以我们需要选取的两者的时候,就要依据当前的场景了.synchronized关键字更加清晰简洁,而ReentrantLock提供了多样化的同步特性,比如超时获取锁,可以终端的获取锁,等待唤醒机制的多个条件变量等等.

参考链接:深入理解Java并发之synchronized实现原理

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容