java并发编程synchronize关键字

1.Synchronized的作用

能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。

2.Synchronized的地位

1.Synchronized是Java的关键字,被Java语言原生支持

2.是最基本的互斥同步手段

3.是并发编程中的元老级角色,是并发编程的必学内容

3.Synchronized的两个性质

1.可重入(synchronized区别于其他锁的一个很重要的性质)

什么是可重入:指的是同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁。也叫做递归锁。Java中两大递归锁:Synchronized和ReentrantLock

好比买车摇号,只要摇到一次号,可以给家里的第一辆车、第二辆车...所有车都上牌,直到我不需要上牌为止。这就叫做可重入性。如果每辆车上牌都需要摇一次号,这就叫做不可重入性。

锁的不可重入性:线程拿到一把锁了,如果想再次使用这把锁,必须先将锁释放,与其他线程再次进行竞争。
锁的可重入性:如果线程已经拿到了锁,试图去请求这个已经获得到的锁,而无需提前释放,直接就可以使用手里的这把锁,这就叫做可重入性

好处:避免死锁、提升封装性

1.假如有两个synchronized修饰的方法1和方法2,此时线程A执行到了方法1,并且获得了方法1的锁,此时方法1调用了方法2,由于方法2也是synchronized修饰的,假设synchronized不具备可重入性的话,那么线程A虽然拿到了方法1的锁,但是由于不可重入,它无法使用本身获得的方法1的这把锁。这样一来,它既想拿锁又不释放锁,这样就会永远等待,形成了死锁。所以由于synchronized具备可重入性,就避免了这种情况的发生。

2.避免了一次又一次的解锁加锁的过程,利用其可重入的性质提高了封装性,简化了并发编程的难度。

粒度:线程而非调用(用三种情况来说明和pthread的区别)

情况1:证明同一个方法是可重入的

情况2:证明可重入不要求是同一个方法

情况3:证明可重入不要求是同一个类中的

2.不可中断(相比于其他有的锁可以中断,这个性质是synchronized的一个劣势所在)

一旦这个锁已经被别的线程获得了,如果当前线程还想获得,只能选择等待或者阻塞,直到别的线程释放这个锁。如果别的线程 永远不释放锁,那么线程只能永远地等下去。

相比之下,Lock类,可以拥有中断的能力。

第一点,如果我觉得我等的时候太长了,有权中断现在已经获取到锁的线程的执行; 第二点,如果我觉得我等待的时间太长了不想再等了,也可以退出。

Lock比synchronized灵活很多,但是编码易出错。

4.Synchronized的两个用法

对象锁

包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)

方法锁形式:synchronized修饰普通方法,锁对象默认为this

代码块形式:手动指定锁对象

类锁

指synchronized修饰静态的方法或指定锁为Class对象

概念(重要):

只有一个Class对象:Java类可能有很多个对象,但只有一个Class对象

本质:所谓的类锁,不过是Class对象的锁而已

用法和效果:类锁只能在同一时刻被一个对象拥有。

形式一:synchronized加在static方法上

形式二:synchronized(*.class)代码块

 

多线程访问同步方法的七种情况(面试常考)

1.两个线程同时访问一个对象的同步方法(对象锁)

这种情况就是对象锁的方法锁情况。会相互等待,只能有一个线程持有锁。

2.两个线程访问的是两个对象的同步方法

不会加锁,因为访问的是不同的实例

3.两个线程访问的是synchronized的静态方法

这种情况就是类锁的静态方法锁。

4.同时访问同步方法与非同步方法

synchronized关键字只作用于当前方法,不会影响其他未加关键字的方法的并发行为。因此非同步方法不受到影响,还是会并发执行。

5.访问同一个对象的不同的普通同步方法(对象锁)

synchronized关键字虽然没有指定所要的那个锁对象,但是本质上是指定了this这个对象作为它的锁。所以对于同一个实例来讲,两个方法拿到的是同一把锁,因此会出现串行的情况。

6.同时访问静态synchronized和非静态synchronized方法

前者为类锁,锁为Class类;后者为对象锁,锁为this对象。因此两者的锁不同,会并行执行。

7.方法抛异常后,会释放锁么?

Lock类加锁时,如果出现异常,不显式手动释放锁的话,Lock是不会释放的。

而synchronized不同,一旦出现异常,会自动释放锁。

也就是说当第二个线程等待一个被synchronized修饰的方法时,若第一个线程出现异常退出,这把锁会立刻释放并且被第二个线程所获取到。JVM会自动把锁释放。

8.扩展:线程进入到一个被synchronized修饰的方法,而在这个方法里面调用了另外一个没有被synchronized修饰的方法,这个时候还是线程安全的吗?

七种情况总结:三点核心思想

1.一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应第1、5种情况);2.每个实例都对应有自己的一把锁,不同实例之间互不影响;
  例外:锁对象是*。class以及synchronized修饰的是static方法的时候,所有对象共用同一把类锁,这就是类锁的两种情况(对应第2、3、4、5种情况);3.无论方法是正常执行完毕或者抛出异常,都会释放锁(对应第7种情况)。

Synchronized的缺陷

1.效率低:

  • 锁的释放情况少

  • 试图获得锁时不能设定超时

  • 不能中断一个正在试图获得锁的线程

2.不够灵活(读写锁更灵活:读操作的时候不会加锁,写操作的时候才会加锁):

  • 加锁和释放的时机单一

  • 每个锁仅有单一的条件(某个对象),可能是不够的

3.无法知道是否成功获取到锁

Lock可以,如果尝试成功了做一些逻辑判断,如果没有成功做另外一些逻辑判断.

Lock类:

lock.lock();lock.unlock();

通过这两个方法,可以手动加锁和解锁。

lock.tryLock();lock.tryLock(10, TimeUnit.MINUTES);

可以判断是否加锁,返回类型为boolean

答案:不是的。出了本方法后,由于另外的方法没有被synchronized修饰,所以说这个方法可以被多个线程同时访问的。

 

总结

一句话介绍synchronized:

JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入性和不可中断的性质。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章