Java并发编程 同步synchronized关键字

1. 介绍

本文介绍Java中的同步synchronized关键字及其使用。

在多线程环境下,如果多个线程同时对变量进行更新操作,可能会造成并发操作的不一致问题,具体原因分析请参考我之前文章《

Java并发编程~并发更新的一致性

》。Java提供同步synchronized机制来解决这个问题。

当一个方法或者一个代码块被标记为synchronized,则同一时间只能有一个线程能够进入执行这个方法或代码块

2. 不同步的问题

下面样例代码中的 方法未加同步,多线程并发操作可能会造成数据不一致:

我们利用一个可以同时运行3个线程的 ,以多线程方式同时执行 1000次,结果输出常常不是1000(具有随机性,每次结果值不同),这个就是多线程并发更新数据不一致造成。

3. synchronized关键字

Java中的synchronized关键字可以解决上述问题,synchronized可以有多种用法,可以分别应用在:

实例方法级别

静态方法级别

代码块上

Java内部使用monitor(也称monitor lock或intrinsic lock)机制实现synchronized同步机制,线程运行同步方法或者代码块,必须先获取相应的monitor锁。

monitor分为两类:

对象实例级别,每个对象实例有一把monitor锁

类级别,每个类有一把monitor锁

3.1 对实例方法使用synchronized关键字

上面的 使用synchronized关键字同步,同时只能有一个线程获得实例锁并进入方法执行。锁在实例级别,有多少个实例就有多少把锁。最后测试程序结果正确是1000。

3.2 对静态方法使用synchronized关键字

上面的 使用synchronized关键字同步,同时只能有一个线程获取类锁并进入方法执行。锁在类级别,因为每个类仅被JVM加载一次,不管类的实例有多少个,类锁只有一把。最后测试程序结果正确是1000。

3.3 对代码块使用synchronized关键字

对代码块同步也分实例级和类级。

有的时候我们不想对整个方法进行同步,则可以对部分代码块进行同步。上面的代码中,我们使用 对代码块进行同步,这个锁也是在 实例级别的。

上面的代码也是同步代码块,但是使用 实现,表示利用类锁进行同步。

4. 结论和注意点

Java中的同步synchronized保证:同一时间只有一个线程能够进入同步块,不可能两个或多个线程同时进入同步块

同步关键字只能用在方法和代码块上。方法和代码块可以是静态的,也可以是非静态的。

当一个线程进入Java同步块,它需要先获取锁;当一个线程离开同步块(正常结束或者异常退出),它会释放锁。

Java的同步synchronized关键字是可重入的,如果线程已经获取了对象锁并执行某个同步方法时,在该方法内它仍然可以调用该对象上的其它同步方法,不需要再获取对象锁。

Java同步机制是有性能开销的,同步方法(或块)相当于一座独木桥,每次只能有一个线程可以过。建议在绝对必要时才考虑同步。另外,尽量缩小锁的作用域(scope),能够用同步块的地方就不要用同步方法。

静态同步方法和非静态同步方法可以并发运行互不干扰,因为锁的级别不同,一个是类锁,一个是实例锁,不是同一把锁。

根据Java语言规范,你不能对构造函数使用synchronized关键字,这种做法编译器通不过。

在Java同步块中,不要synchronize在非final的字段上,非final的字段有可能变化,可能造成不同线程同步在不同实例上,相当于没有同步。建议同步在String类上,它immutable不可变并且是final的。

5. 参考

本文代码已经上传githubhttps://github.com/spring2go/tutorials/tree/master/core-java-concurrency

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180615G1SXJM00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券