Synchronized关键字详解
Posted Kevin张俊杰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Synchronized关键字详解相关的知识,希望对你有一定的参考价值。
前言
线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点:
- 一是存在共享数据(也称临界资源)
- 二是存在多条线程共同操作共享数据
因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。
在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。
Synchronized的使用
-
一把锁只能同时被一个线程获取,没有获得锁的线程只能等待;
-
每个实例都对应有自己的一把锁(this),不同实例之间互不影响;例外:锁对象是*.class以及synchronized修饰的是static方法的时候,所有对象公用同一把锁
-
synchronized修饰的方法,无论方法正常执行完毕还是抛出异常,都会释放锁
对象锁
包括方法锁(默认锁对象为this,当前实例对象)和同步代码块锁(自己指定锁对象)
-
代码块形式:手动指定锁定对象,也可是是this,也可以是自定义的锁
-
方法锁形式:synchronized修饰普通方法,锁对象默认为this
类锁
指synchronize修饰静态的方法或指定锁对象为Class对象
-
synchronize修饰静态方法
-
synchronized指定锁对象为Class对象
深入理解Synchronized
使用Synchronized有哪些要注意的
-
锁对象不能为空,因为锁的信息都保存在对象头里
-
作用域不宜过大,影响程序执行的速度,控制范围过大,编写代码也容易出错
-
避免死锁
-
在能选择的情况下,既不要用Lock也不要用synchronized关键字,用java.util.concurrent包中的各种各样的类
synchronized是公平锁吗
synchronized实际上是非公平的,新来的线程有可能立即获得监视器,而在等待区中等候已久的线程可能再次等待,这样有利于提高性能,但是也可能会导致饥饿现象。
Synchronized的缺陷
-
效率低:锁的释放情况少,只有代码执行完毕或者异常结束才会释放锁;
-
试图获取锁的时候不能设定超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时;
-
不够灵活:加锁和释放的时机单一,每个锁仅有一个单一的条件(某个对象),相对而言,读写锁更加灵活
-
无法知道是否成功获得锁,相对而言,Lock可以拿到状态
Lock解决相应问题
Lock类这里不做过多解释,主要看里面的4个方法:
-
lock(): 加锁
-
unlock(): 解锁
-
tryLock(): 尝试获取锁,返回一个boolean值
-
tryLock(long,TimeUtil): 尝试获取锁,可以设置超时 Synchronized只有锁只与一个条件(是否获取锁)相关联,不灵活,后来Condition与Lock的结合解决了这个问题。
多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。高并发的情况下会导致性能下降。ReentrantLock
的lockInterruptibly()
方法可以优先考虑响应中断。 一个线程等待时间过长,它可以中断自己,然后ReentrantLock响应这个中断,不再让这个线程继续等待。有了这个机制,使用ReentrantLock时就不会像synchronized那样产生死锁了。
ReentrantLock为常用类,它是一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
使用@guardedby同步注解
-
@GuardedBy( "this" )
受对象内部锁保护 -
@GuardedBy( "fieldName" )
受与fieldName引用相关联的锁保护 -
@GuardedBy( "ClassName.fieldName" )
受一个类的静态field的锁保存 -
@GuardedBy( "methodName()" )
锁对象是methodName()方法的返值,受这个锁保护 -
@GuardedBy( "ClassName.class" )
受ClassName类的直接锁对象保护,而不是这个类的某个实例的锁对象
以上是关于Synchronized关键字详解的主要内容,如果未能解决你的问题,请参考以下文章