Java并发编程系列之十 synchronized(1)

Java多线程同步机制
本文深入探讨了Java中多线程并发访问资源时的数据一致性问题及解决方案,详细解析了synchronized关键字作为互斥锁机制的工作原理,包括锁的获取与释放过程,以及其在方法和代码块中的应用。
                       

在多线程并发访问资源(这类资源称为临街资源)的时候,由于割裂来了原子操作,所以会导致数据不一致的情况。为了避免这种情况,需要使用同步机制,同步机制能够保证多线程并发访问数据的时候不会出现数据不一致的情况。

一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁的线程释放锁。在Java中,每个类都有一个内置锁,之所以如此,是因为Java并发专家认为这样可以避免显式创建锁。

通过下面的代码可以直到synchronized到底是如何实现的:

package com.rhwayfun.concurrency;/** * Created by rhwayfun on 16-4-3. */public class Synchronied {    public static void main(String[] args){        synchronized (Synchronied.class){        }        a();    }    public static synchronized void a() {    }}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

使用java -v Synchronized.class命令可以看到如下的结果:

  public static void main(java.lang.String[]);    descriptor: ([Ljava/lang/String;)V    flags: ACC_PUBLIC, ACC_STATIC    Code:      stack=2, locals=3, args_size=1         0: ldc           #2                  // class com/rhwayfun/concurrency/Synchronied         2: dup         3: astore_1         4: monitorenter         5: aload_1         6: monitorexit         7: goto          15        10: astore_2        11: aload_1        12: monitorexit        13: aload_2        14: athrow        15: invokestatic  #3                  // Method a:()V        18: return      Exception table:         from    to  target type             5     7    10   any            10    13    10   any      LineNumberTable:        line 8: 0        line 10: 5        line 11: 15        line 12: 18      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      19     0  args   [Ljava/lang/Stringpublic static synchronized void a();    descriptor: ()V    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED    Code:      stack=0, locals=0, args_size=0         0: return      LineNumberTable:        line 15: 0}SourceFile: "Synchronied.java"
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

通过编译的class文件可以看到synchronized代码块使用了monitorenter和monitorexit两个指令分别获取锁标记和释放锁标记,而synchronized方法使用了ACC_SYNCHRONIZED来完成锁的获取与释放的。也就是锁的获取与释放synchronized关键字自动帮我们完成了。

对于使用synchronized关键字实现的同步机制由如下几点补充说明:

  1. 如果同一个方法有多个线程访问,那么每个线程都有自己的线程拷贝(拷贝存储在工作内存中)
  2. 类的实例都有自己的对象锁,如果一个线程成功获取到该实例的对象锁那么当其他线程需要获取该实例的对象锁时,便需要阻塞等待,直到该实例的对象锁被成功释放。对象锁可以作用在同步方法或者同步代码块中
  3. 如果不同的线程访问的是不同实例的对象锁,那么不会互相阻塞,因为不同实例的对象锁是不同的
  4. 获得实例的对象锁的线程会让其他想要获取相同对象锁的线程阻塞在synchronized代码外,比如由两个synchronized方法a()、b(),那么如果线程A成功进入了同步方法a(),那么该线程便获取了该实例(比如实例obj)的对象锁,而如果其他线程想要执行另一个同步方法b(),就会阻塞在外面,因为a()和b()持有的都是对象obj的对象锁
  5. 持有一个实例的对象锁不会阻止该线程被置换出来,也不会阻塞其他线程执行非synchronized方法,因为非synchronized方法执行的时候不需要获取实例的对象锁
  6. 使用同步代码块的时候,括号的对象可以为任意Object实例,当为this时,指的是当前对象的对象锁
  7. 类锁主要用于控制对static成员变量的并发访问
  8. synchronized块(可以是同步方法或者同步代码块)是可重入的,每次重入会把锁的计数器加1,每次退出将计数器减1,当计数器的值为0的时候,锁便被释放了
  9. Java SE 1.6 为了减少获得锁和释放锁的性能消耗引入了偏向锁和轻量级锁,所以使用synchronized也没有那么重量级了
           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.youkuaiyun.com/jiangjunshow

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值