java并发编程:synchronized同步方法

  • Post author:
  • Post category:java



非线程安全问题:

多个线程对同一个对象中的实例变量进行并发访问时发生,产生脏读,读取的数据可能是被更改过的。

当多个线程同时访问同一个业务对象中没有同步的方法,可能出现非线程安全问题。

解决办法:

使用synchronized声明,保证方法只能由获得锁的线程执行。



在用synchronized声明的方法中,线程进入方法时就获得锁,方法执行完就释放锁。

(1)对于方法内部的私有变量,不存在非线程安全问题。

(2)多个线程同时访问同一个对象中的同步方法一定是线程安全的。


同步synchronized在字节码指令中的原理:


1、synchronized关键字声明的同步方法:

使用flag标记ACC_SYN-CHRONIZED,当调用方法时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志。如果设置,执行线程先持有同步锁,然后再执行方法,最后在方法完成时释放锁。


2、synchronized声明代码块:

使用monitorenter和monitorexit字节码指令进行同步处理。

补充:

同步:按顺序执行业务。

异步:执行A业务同时执行B业务。

注意:

(1)如果线程和业务之间属于一对一的关系,每个线程执行自己所属业务的同步方法,不存在争抢关系,不会出现非线程安全问题。只有多个线程执行相同的业务对象的同步方法时,线程 和业务对象属于多对一的关系,为了避免出现非线程安全问题,使用锁。

(2)调用synchronized声明的方法是排队进行运行的。

(3)如果A线程先持有Object对象的lock锁,B线程如果调用object对象中的synchronized方法,需要等待。

(4)java中,锁就是对象,对象可以映射成锁,哪个线程拿到锁,哪个线程就可以执行这个对象中的synchronized方法

(5)使用synchronized声明非静态方法,那么锁就是调用方法的对象。

(6)出现异常,锁自动释放

(7)通常情况下,一旦持有锁后就不再对锁对象进行更改。

synchronized锁重入:

当一个线程得到一个对象锁时,再次请求此对象锁时可以得到对象锁的。

(1)在synchronized方法/块的内部调用本类的其他synchronized方法/块时,是可以得到锁的。如果得到锁,在没有释放之前可以再次得到锁

(2)锁重入支持继承。当存在父子类继承关系时,子类是完全可以通过锁重入调用父类的同步方法。

synchronized同步语句块:

用synchronized声明方法在某些情况下是有弊端的 。

例如A线程调用同步方法执行一个长时间的任务,那么B线程等待时间就长。

可以 用synchronized语句块来解决。

synchronized代码块是将任意对象作为锁。

当两个并发线程访问同一个对象Object中的synchronzied代码块时,只有获得锁的线程才能执行代码块。

(1)不在synchronized代码块中是异步执行。

(2)synchronized(this)用当前对象作为锁。

(3)同步代码块放在非同步的syn方法中进行声明,并不能保证调用方法的线程执行同步,

方法的调用时无序的。

(4)如果用syn对对象x加锁,那么其他线程执行x对象的同步方法呈现同步效果。


使用class对象作为锁:

Class类用于描述类的基本信息,在内存中设计为单例。

静态同步synchronized方法/synchronized(class)同步块:

(1)synchronized声明静态方法,锁使用当前静态方法所在类对应的Class类的单例对象。

(2)synchronzied(class)使用Class类作为锁。



版权声明:本文为m0_52043808原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。